我试图在集中管理所有子状态的父组件中呈现多个按钮。这意味着父进程使用useState
将每个按钮的点击状态、禁用状态等保存在自己的状态中,并将其作为道具传递给子进程。此外,onClick
函数也在父组件内部定义,并向下传递给每个子组件。目前,我正在这样做:
const [isSelected, setIsSelected] = useState(Array(49).fill(false));
...
const onClick = useCallback((index) => {
const newIsSelected = [...prev];
newIsSelected[i] = !newIsSelected[i];
return newIsSelected;
}, []);
...
(In the render function:)
return isSelected.map((isFieldSelected, key) => {
<React.Fragment key={key}>
<TheChildComponent
isSelected={isFieldSelected}
onClick={onClick}
/>
</React.Fragment/>
})
为了防止子组件重新渲染,我使用…
- …
useCallback
使反应看到onClick功能始终保持不变 - …
React.Fragment
使react再次找到组件,否则子组件将没有唯一的id或类似的东西
子组件导出为:
export default React.memo(TheChildComponent, compareEquality)
with
const compareEquality = (prev, next) => {
console.log(prev, next);
return prev.isSelected === next.isSelected;
}
不知何故compareEquality
中的日志行从未执行过,因此我知道compareEquality
从未执行过。我也不知道为什么会这样。
我已经检查了所有的博客,以前的Stackoverflow问题等,但还没有找到一种方法来防止子组件被重新渲染,每次至少有一个组件执行onClick
函数,并通过这样做更新了isSelected
状态。
如果有人能给我指出正确的方向或解释我的问题从何而来,我将非常高兴。
提前感谢!
这段代码实际上将在每次渲染时生成一个新的onClick
函数,因为useCallback
没有给定一个deps数组:
const onClick = useCallback((index) => {
const newIsSelected = [...prev];
newIsSelected[i] = !newIsSelected[i];
return newIsSelected;
});
下面应该只创建一个onClick
函数,并在所有渲染中重用它:
const onClick = useCallback((index) => {
const newIsSelected = [...prev];
newIsSelected[i] = !newIsSelected[i];
return newIsSelected;
}, []);
与香草React.memo
相结合,这应该可以防止子渲染,除非isSelected
发生变化。(你对React.memo
的第二个论点应该也解决了这个问题——我不知道为什么不工作。)
<React.Fragment key={key}>
<TheChildComponent
isSelected={isFieldSelected}
onClick={onClick}
/>
</React.Fragment/>
到下面:
<TheChildComponent key={key}
isSelected={isFieldSelected}
onClick={onClick}
/>
(假设您确实只需要在map
的主体中使用一个组件)。
事实证明,唯一的问题既不是useCallback
,也不是useMemo
或任何类似的东西。
在父组件的渲染函数中,我没有直接使用
return isSelected.map(...)
我从一个单独的,非常简单的组件中包含了这个部分,就像这样:
const Fields = () => {
return isSelected.map((isFieldSelected, i) => (
<TheChildComponent
key={i}
isSelected={isFieldSelected}
onClick={onClick}
/>
));
};
这就是我的问题所在。当将代码从单独的组件Fields
移动到父组件的返回语句中时,重呈现错误就消失了。
还是谢谢你的帮助。