如何避免在父组件状态更新时重新发送循环中的所有子组件



我有一个子组件,它在父组件的循环中。当其中一个子组件更新父组件的状态时,由于它是循环的,因此它将重新呈现所有子组件。如何避免每次迭代都要重新渲染。


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>
);
}

当前行为:在上面的代码中,当我单击其中一个子组件中的复选框时,它正在更新父组件状态(selectedChild(。因此,循环正在执行,所有子级(所有表行(都在重新呈现。

预期行为:只有该特定行需要重新渲染

演示:https://codesandbox.io/s/newpro-0pezc

为此,您可以使用React.memo,如果道具保持不变,它将存储您的组件。但考虑到您的代码,您需要进行一些额外的更改:

  • 您必须应用useCallback来记忆onChangeHandle函数;

  • 要在ChangeHandle上正确地进行内存化,您需要对其进行重构。您不能直接传递selectedChild,否则它会对其值进行内存化。使用setSelectedChild作为参数传递一个采用selectedChild的函数。

  • 您的孩子应该接收isSelected作为布尔值而不是函数。否则道具将保持不变,Child永远不会更新;

    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

基本答案是在Child上使用React.memo

const Child = memo(function Child(...) {...})

但是,为了使memo工作,如果不应该重新绘制,则该组件需要接收相同的道具。这意味着在onChangeHandle:上使用useCallback

const onChangeHandle = useCallback((event, id) => {...}, [])

但是由于onChangeHandle使用的selectedChild总是在复选框更改时更改,因此您还需要使用useRef:来引用它

const selectedChildRef = useRef();
selectedChildRef.current = selectedChild;

并在CCD_ 15内部使用reffed版本。

最后需要做的是将isSelected道具从函数更改为仅一个标志,因为它需要在每个复选框更改时运行:

isSelected={selectedChild.includes(index)}

https://codesandbox.io/s/newpro-forked-wxvqs

您可以实现shouldComponentUpdate(文档:https://reactjs.org/docs/react-component.html#shouldcomponentupdate)在Child的定义中,当它重新发布时有更多的控制权。但这只适用于出现性能问题的情况——通常你不必担心,让它们全部重新发布是标准的。

相关内容

  • 没有找到相关文章

最新更新