具有数百个输入的巨大 React 状态数组,缓慢的状态变化 onChange
Huge React State Array with Hundreds of Inputs, slow state changes onChange
我正在尝试使用大型 React 表单,但表单中的每个输入都很复杂并且具有复杂的状态。我将所有输入的状态保存在 parent
组件中,每个输入都包装在 child
组件中。我在父级中有一个状态数组,其中包含所有输入元素的状态的当前值。目前,每次输入中有 onChange
时,我都会尝试通过 setState()
重置父级中的整个状态数组。这对于最多 5 个输入是没问题的,但是一旦我超过这个数量(比如一百个输入),我开始注意到程序中存在一些严重的滞后。 请注意:该程序还允许您重新排列 delete
和 add
输入,因此状态需要适应这些变化 即。第一个输入可以与第二个输入交换位置,或者在第 10 个输入之后插入。
我的objective就是想办法优化这个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,其他人似乎建议使用 useMemo
和 useEffect
的组合来防止重新渲染。非常感谢您的帮助。
我注意到的一件事是,如果我尝试在子组件中保持单独的状态,它们呈现 onChange
的速度会快得多。这可能是因为它不必每次都为父状态数组设置状态。如果可能的话,我希望能够在单击保存时简单地遍历并获取子节点的状态。在这种情况下我会使用 ref
吗?可以不用ref吗?
我也想避免使用 onBlur()
只是为了这个项目的目的
codesandbox复制如下供参考:https://codesandbox.io/s/cocky-knuth-jm8n6?fontsize=14
创建了具有相似功能组件和虚拟数据的示例:
https://codesandbox.io/s/recursing-cdn-q4vx0
必须创建具有本地项状态的子组件并与主数组同步。
添加删除和添加。
使用对象数组作为示例。
我会最小化 re-renders,所以我会执行以下操作:
- 将所有函数保留在 parent 中并共享 memoized 回调以避免冗余函数构造
- 不使用索引来找出具体的children
- 记忆 children
查看已实施删除的代码
这样,您可以将 re-renders 减少到几乎为零,并且您可以继续使用,直到您面临复制和渲染非常大的数组的惩罚,我猜您不太可能 运行 进入
您有 2 个选择:
将 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}
/>
))}
正如您提到的 state
数组很大,您不应该再使用 .map()
,而应该使用诸如 react-virtualized 之类的库,它只会将可见元素渲染到 DOM 并大大提高您的性能。
复制大型数组不应该是罪魁祸首,它只复制通常非常快的对象引用,而不是真正的深层复制。事实上,Redux itself就是这样做的。
您遇到此延迟的原因是您没有为子项设置密钥。您应该设置唯一键,不要使用索引作为键。
我正在尝试使用大型 React 表单,但表单中的每个输入都很复杂并且具有复杂的状态。我将所有输入的状态保存在 parent
组件中,每个输入都包装在 child
组件中。我在父级中有一个状态数组,其中包含所有输入元素的状态的当前值。目前,每次输入中有 onChange
时,我都会尝试通过 setState()
重置父级中的整个状态数组。这对于最多 5 个输入是没问题的,但是一旦我超过这个数量(比如一百个输入),我开始注意到程序中存在一些严重的滞后。 请注意:该程序还允许您重新排列 delete
和 add
输入,因此状态需要适应这些变化 即。第一个输入可以与第二个输入交换位置,或者在第 10 个输入之后插入。
我的objective就是想办法优化这个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,其他人似乎建议使用 useMemo
和 useEffect
的组合来防止重新渲染。非常感谢您的帮助。
我注意到的一件事是,如果我尝试在子组件中保持单独的状态,它们呈现 onChange
的速度会快得多。这可能是因为它不必每次都为父状态数组设置状态。如果可能的话,我希望能够在单击保存时简单地遍历并获取子节点的状态。在这种情况下我会使用 ref
吗?可以不用ref吗?
我也想避免使用 onBlur()
只是为了这个项目的目的
codesandbox复制如下供参考:https://codesandbox.io/s/cocky-knuth-jm8n6?fontsize=14
创建了具有相似功能组件和虚拟数据的示例:
https://codesandbox.io/s/recursing-cdn-q4vx0
必须创建具有本地项状态的子组件并与主数组同步。 添加删除和添加。
使用对象数组作为示例。
我会最小化 re-renders,所以我会执行以下操作:
- 将所有函数保留在 parent 中并共享 memoized 回调以避免冗余函数构造
- 不使用索引来找出具体的children
- 记忆 children
这样,您可以将 re-renders 减少到几乎为零,并且您可以继续使用,直到您面临复制和渲染非常大的数组的惩罚,我猜您不太可能 运行 进入
您有 2 个选择:
将
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} /> ))}
正如您提到的
state
数组很大,您不应该再使用.map()
,而应该使用诸如 react-virtualized 之类的库,它只会将可见元素渲染到 DOM 并大大提高您的性能。
复制大型数组不应该是罪魁祸首,它只复制通常非常快的对象引用,而不是真正的深层复制。事实上,Redux itself就是这样做的。
您遇到此延迟的原因是您没有为子项设置密钥。您应该设置唯一键,不要使用索引作为键。