Array.map 中的 React 功能组件在将函数作为 props 传递时总是重新渲染
React functional components in Array.map are always rerendering when getting passed a function as props
我正在尝试在集中管理所有 child 状态的 parent 组件中呈现多个按钮。这意味着 parent 存储例如单击状态,每个按钮的禁用状态使用 useState
并将其作为道具传递给 child。此外,onClick
函数也在 parent 组件内部定义,并传递给每个 child。目前我正在这样做:
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/>
})
为了防止 child 组件重新渲染,我正在使用...
- ...
useCallback
让 react 看到 onClick 函数始终保持不变
- ...
React.Fragment
让反应再次找到一个组件,否则 child 将没有唯一的 ID 或类似的东西
child 组件导出为:
export default React.memo(TheChildComponent, compareEquality)
和
const compareEquality = (prev, next) => {
console.log(prev, next);
return prev.isSelected === next.isSelected;
}
不知何故 compareEquality
中的日志行从未执行过,因此我知道 compareEquality
从未执行过。我也不知道为什么会这样。
我检查了所有博客、以前的 Whosebug 问题等,但还没有找到一种方法来防止每次至少有一个组件执行 onClick
函数时重新呈现 child 组件并通过这样做更新了 isSelected
状态。
如果有人能指出正确的方向或解释我的问题出在哪里,我将非常高兴。
提前致谢!
此代码实际上会在每次渲染时生成一个新的 onClick
函数,因为 useCallback
没有给出 deps 数组:
const onClick = useCallback((index) => {
const newIsSelected = [...prev];
newIsSelected[i] = !newIsSelected[i];
return newIsSelected;
});
以下应该只创建一个 onClick
函数,并且 re-use 它遍及所有渲染:
const onClick = useCallback((index) => {
const newIsSelected = [...prev];
newIsSelected[i] = !newIsSelected[i];
return newIsSelected;
}, []);
与原版 React.memo
相结合,这应该可以防止 children 变为 re-rendering,除非 isSelected
发生变化。 (你对 React.memo
的第二个论点应该也解决了这个问题——我不确定为什么它不起作用。)
作为旁注,您可以简化此代码:
<React.Fragment key={key}>
<TheChildComponent
isSelected={isFieldSelected}
onClick={onClick}
/>
</React.Fragment/>
以下内容:
<TheChildComponent key={key}
isSelected={isFieldSelected}
onClick={onClick}
/>
(假设您确实只需要 map
主体中的一个组件)。
事实证明,唯一的问题既不是 useCallback
、useMemo
也不是类似问题。
在父组件的render函数中我没有直接使用
return isSelected.map(...)
我从一个单独的、非常简单的组件中包含了该部分,如下所示:
const Fields = () => {
return isSelected.map((isFieldSelected, i) => (
<TheChildComponent
key={i}
isSelected={isFieldSelected}
onClick={onClick}
/>
));
};
这就是我的问题所在。将代码从单独的组件 Fields
移动到父组件的 return 语句时,重新呈现错误消失了。
不过,谢谢你的帮助。
我正在尝试在集中管理所有 child 状态的 parent 组件中呈现多个按钮。这意味着 parent 存储例如单击状态,每个按钮的禁用状态使用 useState
并将其作为道具传递给 child。此外,onClick
函数也在 parent 组件内部定义,并传递给每个 child。目前我正在这样做:
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/>
})
为了防止 child 组件重新渲染,我正在使用...
- ...
useCallback
让 react 看到 onClick 函数始终保持不变 - ...
React.Fragment
让反应再次找到一个组件,否则 child 将没有唯一的 ID 或类似的东西
child 组件导出为:
export default React.memo(TheChildComponent, compareEquality)
和
const compareEquality = (prev, next) => {
console.log(prev, next);
return prev.isSelected === next.isSelected;
}
不知何故 compareEquality
中的日志行从未执行过,因此我知道 compareEquality
从未执行过。我也不知道为什么会这样。
我检查了所有博客、以前的 Whosebug 问题等,但还没有找到一种方法来防止每次至少有一个组件执行 onClick
函数时重新呈现 child 组件并通过这样做更新了 isSelected
状态。
如果有人能指出正确的方向或解释我的问题出在哪里,我将非常高兴。
提前致谢!
此代码实际上会在每次渲染时生成一个新的 onClick
函数,因为 useCallback
没有给出 deps 数组:
const onClick = useCallback((index) => {
const newIsSelected = [...prev];
newIsSelected[i] = !newIsSelected[i];
return newIsSelected;
});
以下应该只创建一个 onClick
函数,并且 re-use 它遍及所有渲染:
const onClick = useCallback((index) => {
const newIsSelected = [...prev];
newIsSelected[i] = !newIsSelected[i];
return newIsSelected;
}, []);
与原版 React.memo
相结合,这应该可以防止 children 变为 re-rendering,除非 isSelected
发生变化。 (你对 React.memo
的第二个论点应该也解决了这个问题——我不确定为什么它不起作用。)
作为旁注,您可以简化此代码:
<React.Fragment key={key}>
<TheChildComponent
isSelected={isFieldSelected}
onClick={onClick}
/>
</React.Fragment/>
以下内容:
<TheChildComponent key={key}
isSelected={isFieldSelected}
onClick={onClick}
/>
(假设您确实只需要 map
主体中的一个组件)。
事实证明,唯一的问题既不是 useCallback
、useMemo
也不是类似问题。
在父组件的render函数中我没有直接使用
return isSelected.map(...)
我从一个单独的、非常简单的组件中包含了该部分,如下所示:
const Fields = () => {
return isSelected.map((isFieldSelected, i) => (
<TheChildComponent
key={i}
isSelected={isFieldSelected}
onClick={onClick}
/>
));
};
这就是我的问题所在。将代码从单独的组件 Fields
移动到父组件的 return 语句时,重新呈现错误消失了。
不过,谢谢你的帮助。