问题描述
我有一个位于父组件循环内的子组件.当其中一个子组件正在更新父组件的状态时,它会重新渲染所有子组件,因为它是循环的.如何避免每次迭代重新渲染.
函数父(){const [selectedChild, setSelectedChild] = useState([]);const onChangeHandle = (event, id) =>{const 检查 = event.target.checked;让 updatedArray = [...selectedChild];如果(检查){if(!selectedChild.includes(id)){updatedArray.push(id);}}别的{var index = updatedArray.indexOf(id)如果(索引!== -1){updatedArray.splice(index, 1);}}setSelectedChild(updatedArray);}const dummy = (id) =>{返回 selectedChild.includes(id);}返回 (<div><表格>{[1,2,3].map((value, index) => {返回 (<孩子键={索引}索引={索引}价值={价值}句柄={onChangeHandle}isSelected={dummy}/>)})}</tbody><div>{selectedChild}</div>)}function Child({index, value, handle, isSelected }) {console.log('渲染')返回 (<tr><td>句柄(事件,索引)}/></td><td>hello {index} {value}</td></tr>)}导出默认函数 App() {返回 (
);}
当前行为:在上面的代码中,当我单击子组件之一中的复选框时,它正在更新父组件状态(selectedChild
).所以循环正在执行,所有子项(所有表行)都在重新渲染.
预期行为:只有该特定行必须重新渲染
演示: https://codesandbox.io/s/newpro-0pezc
为此你可以使用 React.memo 如果 props 保持不变,它会记住你的组件.但是鉴于您的代码,您需要进行一些额外的更改:
你必须应用
useCallback
来记住onChangeHandle函数;要正确记住 onChangeHandle,您需要重构它.你不能直接传递
selectedChild
,否则它会记住它的值.使用setSelectedChild
作为参数传递一个接受selectedChild
的函数.你的孩子应该接收
isSelected
作为布尔值而不是函数.否则 props 将保持不变并且 Child 永远不会更新;import React, { useState, memo, useCallback } from "react";函数父(){const [selectedChild, setSelectedChild] = useState([]);const onChangeHandle = useCallback((event, id) => {setSelectedChild(selectedChild => {const 检查 = event.target.checked;让 updatedArray = [...selectedChild];如果(检查){如果 (!selectedChild.includes(id)) {updatedArray.push(id);}} 别的 {var index = updatedArray.indexOf(id);如果(索引!== -1){updatedArray.splice(index, 1);}}返回更新数组;});}, []);const 虚拟 = id =>{返回 selectedChild.includes(id);};const renderChildren = () =>[1, 2, 3].map((value, index) => {返回 (<孩子键={索引}索引={索引}价值={价值}句柄={onChangeHandle}isSelected={dummy(index)}/>);});返回 (<div><表格><tbody>{renderChildren()}</tbody><div>{selectedChild}</div>
);}const Child = memo(({ index, value, handle, isSelected }) => {console.log(渲染");返回 (<tr><td>句柄(事件,索引)}/></td><td>你好 {index} {value}</td></tr>);});导出默认函数 App() {返回 (
<父/>);}
https://stackblitz.com/edit/so-memo-children?file=src/App.js
I m having one child component which is inside a loop of parent component. when one of the child components is updating the state of parent component, it is re-rendering the all children since it is loop. How can i avoid the re-render for each iteration.
function Parent() {
const [selectedChild, setSelectedChild] = useState([]);
const onChangeHandle = (event, id) => {
const checked = event.target.checked;
let updatedArray = [...selectedChild];
if(checked){
if(!selectedChild.includes(id)){
updatedArray.push(id);
}
}
else{
var index = updatedArray.indexOf(id)
if (index !== -1) {
updatedArray.splice(index, 1);
}
}
setSelectedChild(updatedArray);
}
const dummy = (id) => {
return selectedChild.includes(id);
}
return (
<div>
<table>
<tbody>
{[1,2,3].map((value, index) => {
return (
<Child
key={index}
index={index}
value={value}
handle={onChangeHandle}
isSelected={dummy}
/>
)
})}
</tbody>
</table>
<div>
{selectedChild}
</div>
</div>)
}
function Child({index, value, handle, isSelected }) {
console.log('rendering')
return (
<tr>
<td>
<input
type="checkbox"
checked={isSelected(index)}
onChange={(event) => handle(event, index)}/>
</td>
<td>hello {index} {value}</td>
</tr>
)
}
export default function App() {
return (
<div className="App">
<Parent />
</div>
);
}
Current behaviour:In above code, When i m clicking on the checkbox in one of the children component, it is updating the parent component state(selectedChild
). So the loop is executing and all children(all table rows) are re rendering.
Expected behaviour:Only that particular row have to go for re-render
Demo: https://codesandbox.io/s/newpro-0pezc
for that you can use React.memo that will memoize your component if props remains the same. But given your code you need to make some extra changes:
you have to apply
useCallback
to memoize onChangeHandle function;to memoize properly onChangeHandle you need to refactor it. you can't pass
selectedChild
directly, otherwise it memoizes its value. usesetSelectedChild
passing as argument a function that takesselectedChild
instead.your Child should receive
isSelected
as boolean value instead of function. otherwise props will remain the same and Child never updates;import React, { useState, memo, useCallback } from "react"; function Parent() { const [selectedChild, setSelectedChild] = useState([]); const onChangeHandle = useCallback((event, id) => { setSelectedChild(selectedChild => { const checked = event.target.checked; let updatedArray = [...selectedChild]; if (checked) { if (!selectedChild.includes(id)) { updatedArray.push(id); } } else { var index = updatedArray.indexOf(id); if (index !== -1) { updatedArray.splice(index, 1); } } return updatedArray; }); }, []); const dummy = id => { return selectedChild.includes(id); }; const renderChildren = () => [1, 2, 3].map((value, index) => { return ( <Child key={index} index={index} value={value} handle={onChangeHandle} isSelected={dummy(index)} /> ); }); return ( <div> <table> <tbody>{renderChildren()}</tbody> </table> <div>{selectedChild}</div> </div> ); } const Child = memo(({ index, value, handle, isSelected }) => { console.log("rendering"); return ( <tr> <td> <input type="checkbox" checked={isSelected} onChange={event => handle(event, index)} /> </td> <td> hello {index} {value} </td> </tr> ); }); export default function App() { return ( <div className="App"> <Parent /> </div> ); }
https://stackblitz.com/edit/so-memo-children?file=src/App.js
这篇关于如何避免在父组件状态更新时重新渲染循环中的所有子组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!