React 有状态列表的设计,其中每个项目都依赖于前一个项目

问题描述 投票:0回答:1

我正在使用 React 开发一个项目,我需要创建一些列表,其中每个项目代表一个表行,并且依赖于上一行(部分依赖于下一行) - 类似于 Excel 或 Google Sheet有效。
假设每个项目都有三个字段:

a
b
c
。每个项目根据前一项的
a
字段更新自己的
a
字段,并根据下一项的
b
字段更新其
c
字段。

我当前的设计是将每个项目保存为一个单独的组件(呈现表格行),并在父组件中保存 2 个列表 - 一个用于每个项目的状态,另一个用于项目本身。
看起来像这样:

interface ItemState {
  a: number,
  b: number,
  c: number,
}

function ItemComponent({prevState, nextState, state, setState}:
        {prevState: ItemState; nextState: ItemState; state: ItemState; setState: (state: ItemState) => void;}) {

  React.useEffect(() => {
    setState({...state, a: prevState.a+1});
  }, [prevState.a]);

  React.useEffect(() => {
    setState({...state, b: nextState.c-1});
  }, [nextState.c]);

  return (<...>); // table row
}

function ParentComponent() {
  const [stateList, setStateList] = React.useState<ItemState[]>([item1, item2, item3]);
  
  const mapState = React.useCallback(
    (prev: ItemState | null, curr: ItemState, next: ItemState | null) => {
      return (
        <ItemComponent
          key="someUniqueId"
          prevState={prev}
          nextState={next}
          state={curr}
          setState="some function which sets a specific item in the list based on id"
        />
      );
    },
    []
  );

  const itemList = React.useMemo(
    () =>
      stateList.map((state, i) => {
        const prev = i === 0 ? null : stateList[i - 1];
        const next = i === stateList.length - 1 ? null : stateList[i + 1];
        return mapState(prev, stintState, next);
      }),
    [stateList, mapState]
  );

  return (<...>); // rendered table containing the itemList
}

(显然这是一个简化版本,实际上我在更多字段上执行更复杂的计算,而不仅仅是

a
b
c
。)

这个解决方案有效。然而,它的性能很糟糕,因为我通常在列表中拥有的不仅仅是 3 个项目,导致当更新一个项目时需要花费大量时间来更新所有后代。
此外,当列表包含超过 14 个项目时,我开始收到“超出最大更新深度”错误,这是由于

setState
内有大量
useEffect
导致的。

我确信这个问题有一个更优雅、更高效的解决方案,我只是自己无法弄清楚 - 我很想在这方面获得一些帮助。

reactjs typescript react-hooks state
1个回答
0
投票

我相信你已经创建了一个无限循环。如果更改一个项目的状态,下一个项目和上一个项目的状态都会发生变化,并且由于这些状态发生变化,those 项目的上一个和下一个项目会更改状态(包括已更改的原始项目,开始循环所有项目)一遍又一遍,无限)。

这是一个棘手的问题,但我认为简化的第一步是不让任何项目接受任何其他项目的状态作为道具,然后弄清楚逻辑需要如何工作。我还认为每个单元格应该有自己的状态,而不是行。渲染逻辑可能全部需要在网格的父组件内进行,该组件将仅将更改的道具传递给道具根据该逻辑而更改的项目。

但是,仍然有一些方法可以创建无限循环,您必须考虑到这一点。例如,在项目 1A 设置为 2A + 1,项目 2A 设置为 1A + 1,您现在创建了另一个无限循环。您将需要您的逻辑可以测试并抛出错误的规则,而不是无限循环。更难测试的情况是 1A = 2A + 1、2A = 3A + 1、3A = 1A + 1,这会导致无限循环,其中没有一个单元格引用另一个引用它们的单元格。您必须思考/研究一种方法来检测这些自引用公式。

© www.soinside.com 2019 - 2024. All rights reserved.