React efficiently update object in array with useState hook
我有一个React组件,可以渲染一个相当大的输入列表(100个项)。它可以在我的计算机上正常显示,但是手机上的输入延迟明显。 React DevTools显示每次按键时整个父对象都将重新呈现。
有没有更有效的方法来解决这个问题?
https://codepen.io/anon/pen/YMvoyy?editors=0011
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | function MyInput({obj, onChange}) { return ( <label> {obj.label} <input type="text" value={obj.value} onChange={onChange} /> </label> ); } // Passed in from a parent component const startingObjects = new Array(100).fill(null).map((_, i) => ({label: i, value: 'value'})); function App() { const [objs, setObjects] = React.useState(startingObjects); function handleChange(obj) { return (event) => setObjects(objs.map((o) => { if (o === obj) return {...obj, value: event.target.value} return o; })); } return ( {objs.map((obj) => <MyInput obj={obj} onChange={handleChange(obj)} />)} ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement); |
该问题与:
1 2 3 4 5 6 | function handleChange(obj) { return (event) => setObjects(objs.map((o) => { if (o === obj) return {...obj, value: event.target.value} return o; })); } |
在此,您将更新
如果给定相同的道具,功能组件呈现相同的结果,则可以将其包装在对React.memo的调用中以提高性能。
https://reactjs.org/docs/react-api.html#reactmemo
1 2 3 4 5 6 7 | const MyInput = React.memo(({obj, onChange}) => { console.log(`Rerendered: ${obj.label}`); return <label>{obj.label}</label> <input type="text" value={obj.value} onChange={onChange} /> ; }, (prevProps, nextProps) => prevProps.obj.label === nextProps.obj.label && prevProps.obj.value === nextProps.obj.value); |
但是,React.Memo仅在尝试确定是否应渲染时才进行浅表比较,因此我们可以将自定义比较函数作为第二个参数传递。
1 | (prevProps, nextProps) => prevProps.obj.label === nextProps.obj.label && prevProps.obj.value === nextProps.obj.value); |
基本上来说,如果
最后,
1 2 3 4 5 6 7 8 9 | function handleChange(obj) { return (event) => { const value = event.target.value; setObjects(prevObjs => (prevObjs.map((o) => { if (o === obj) return {...obj, value } return o; }))) }; } |
https://codepen.io/anon/pen/QPBLwy?editors=0011拥有所有这些内容以及console.logs,显示是否重新渲染了某些内容。
Is there a more efficient way to approach this?
您将所有值存储在一个数组中,这意味着您不知道在不迭代整个数组的情况下需要比较哪个元素,就比较对象是否匹配。
如果从对象开始:
1 2 | const startingObjects = new Array(100).fill(null).reduce((objects, _, index) => ({...objects, [index]: {value: 'value', label: index}}), {}) |
进行一些修改后,您的handle函数将更改为
1 2 3 4 5 6 | function handleChange(obj, index) { return (event) => { const value = event.target.value; setObjects(prevObjs => ({...prevObjs, [index]: {...obj, value}})); } } |
https://codepen.io/anon/pen/LvBPEB?editors=0011作为示例。