具有数百个输入的巨大 React 状态数组,缓慢的状态变化 onChange

Huge React State Array with Hundreds of Inputs, slow state changes onChange

我正在尝试使用大型 React 表单,但表单中的每个输入都很复杂并且具有复杂的状态。我将所有输入的状态保存在 parent 组件中,每个输入都包装在 child 组件中。我在父级中有一个状态数组,其中包含所有输入元素的状态的当前值。目前,每次输入中有 onChange 时,我都会尝试通过 setState() 重置父级中的整个状态数组。这对于最多 5 个输入是没问题的,但是一旦我超过这个数量(比如一百个输入),我开始注意到程序中存在一些严重的滞后。 请注意该程序还允许您重新排列 deleteadd 输入,因此状态需要适应这些变化 即。第一个输入可以与第二个输入交换位置,或者在第 10 个输入之后插入。

我的objective就是想办法优化这个onChange的性能。最后,我真的不需要数据在 parent 组件中,我只需要在单击页面底部的 save 时收集输入值。

重申一下,我有两个组成部分。

  1. 父组件

  2. 一个子组件

Child 组件基本上是一个 input,用户可以在其中编写数百行文本。

Parent 组件包含 100 个子组件,基本上如下所示:

export default function Parent(props) {
  const [state, setState] = useState(createDummyData());
  useEffect(() => {});



  const onInputChange = (value, index) => {
    var tempValue = [...state];
    tempValue[index] = value;
    setState(tempValue);
  };

  return (
    <>
      <div style={{ display: "flex", flexDirection: "column" }}>
        {state.map((item, index) => (
          <Child
            value={state[index].content}
            index={index}
            onChange={onInputChange}
          />
        ))}
        <button style={{backgroundColor: "red"}}>save input data</button>
      </div>
    </>
  );
}

子组件如下所示

export default function Child(props) {
  useEffect(() => {});

  const onChange = event => {
    props.onChange(event.target.value, props.index);
  };

  return (
    <>
      <input value={props.value} onChange={onChange} />
    </>
  );
}

我还没有找到解决这个问题的简单方法。有些人似乎建议使用 Redux,其他人似乎建议使用 useMemouseEffect 的组合来防止重新渲染。非常感谢您的帮助。

我注意到的一件事是,如果我尝试在子组件中保持单独的状态,它们呈现 onChange 的速度会快得多。这可能是因为它不必每次都为父状态数组设置状态。如果可能的话,我希望能够在单击保存时简单地遍历并获取子节点的状态。在这种情况下我会使用 ref 吗?可以不用ref吗?

我也想避免使用 onBlur() 只是为了这个项目的目的

codesandbox复制如下供参考:https://codesandbox.io/s/cocky-knuth-jm8n6?fontsize=14

创建了具有相似功能组件和虚拟数据的示例:

https://codesandbox.io/s/recursing-cdn-q4vx0

必须创建具有本地项状态的子组件并与主数组同步。 添加删除和添加。

使用对象数组作为示例。

我会最小化 re-renders,所以我会执行以下操作:

  1. 将所有函数保留在 parent 中并共享 memoized 回调以避免冗余函数构造
  2. 不使用索引来找出具体的children
  3. 记忆 children

您可以通过 link https://codesandbox.io/s/loving-rgb-cejoy?fontsize=14&hidenavigation=1&theme=dark&file=/src/Parent.jsx

查看已实施删除的代码

这样,您可以将 re-renders 减少到几乎为零,并且您可以继续使用,直到您面临复制和渲染非常大的数组的惩罚,我猜您不太可能 运行 进入

您有 2 个选择:

  1. key 添加到 Child,例如电子邮件、名字、姓氏,允许 React 仅重新呈现更改的输入。最好不要对 key 使用 index,否则状态数组中的 inserting/deleting 项将导致整个列表重新呈现

    {state.map((item, index) => (
      <Child
        // e.g., email, firstName, lastName, prefer not to use `index`
        key={item.name}
        value={item.content}
        index={index}
        onChange={onInputChange}
      />
    ))}
    
  2. 正如您提到的 state 数组很大,您不应该再使用 .map(),而应该使用诸如 react-virtualized 之类的库,它只会将可见元素渲染到 DOM 并大大提高您的性能。

复制大型数组不应该是罪魁祸首,它只复制通常非常快的对象引用,而不是真正的深层复制。事实上,Redux itself就是这样做的。

您遇到此延迟的原因是您没有为子项设置密钥。您应该设置唯一键,不要使用索引作为键。