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

Posted

技术标签:

【中文标题】具有数百个输入的巨大 React 状态数组,状态变化缓慢 onChange【英文标题】:Huge React State Array with Hundreds of Inputs, slow state changes onChange 【发布时间】:2020-02-25 11:19:15 【问题描述】:

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

我的目标是找到一种方法来优化这个 onChange 的性能。最终,我真的不需要将数据放在parent 组件中,我只需要在单击页面底部的save 时收集输入的值。

重申一下,我有两个组件。

    父组件

    子组件

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 吗?没有参考可以做到吗?

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

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

【问题讨论】:

您没有设置导致延迟的主要原因 【参考方案1】:

使用类似的功能组件和虚拟数据创建示例:

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

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

使用对象数组作为示例。

【讨论】:

我有一个小问题,我必须将状态设为数组,而不是地图,因为可以添加或删除输入。您将如何修改它以使其能够完成此任务? 您是否尝试删除现有地图中的道具,例如:删除状态[keyfield],您也可以使用数组,但您需要一些字段来为每一行提供键唯一值。 我用你的代码更新答案并添加删除和更新 谢谢!!!!这真太了不起了!只是一个快速的问题。我对你是如何做到的有点困惑: const onInputChange = (index, item) => state[index] = item; ;状态不应该是不可变的吗?你不应该使用 setState() 吗? 是的,你是对的,但在这种情况下,我只想将新实例从子项同步到父数组而不重新渲染整个数组,仅用于同步。【参考方案2】:

我会尽量减少重新渲染,所以我会做以下事情:

    将所有函数保留在父级中并共享记忆回调以避免冗余函数构造 不要使用索引来找出特定的孩子 记住孩子们

您可以通过链接https://codesandbox.io/s/loving-rgb-cejoy?fontsize=14&hidenavigation=1&theme=dark&file=/src/Parent.jsx查看已实现删除的代码

这样,您将重新渲染几乎减少到零,并且您可以继续进行,直到您面临复制和渲染非常大的数组的惩罚,我猜您不太可能遇到

【讨论】:

【参考方案3】:

你有两个选择:

    key 添加到Child,例如电子邮件、名字、姓氏,允许 React 仅重新呈现更改的输入。 key最好不要使用index,否则在状态数组的存在处插入/删除项会导致整个列表重新渲染

    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
      />
    ))
    

    正如您提到的state 数组很大,您不应该再使用.map(),而应该使用诸如react-virtualized 之类的库,它只会将可见元素呈现给DOM,并大大提高您的性能。

【讨论】:

【参考方案4】:

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

您遇到这种滞后的原因是您没有为孩子设置钥匙。您应该设置唯一键,并且不要使用索引作为键。

【讨论】:

这与按键无关

以上是关于具有数百个输入的巨大 React 状态数组,状态变化缓慢 onChange的主要内容,如果未能解决你的问题,请参考以下文章

状态模式中的最大状态数

在 React 中渲染具有不同状态的组件数组

几行 Python 代码就可以提取数百个时间序列特征

如何将用户输入的输入保存为 React 中的数组状态

React + Redux - 当输入具有来自状态的值时,输入 onChange 非常慢

具有数百个数据库连接的 MVC 应用程序