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 组件重新渲染,我正在使用...

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 主体中的一个组件)。

事实证明,唯一的问题既不是 useCallbackuseMemo 也不是类似问题。

在父组件的render函数中我没有直接使用

return isSelected.map(...)

我从一个单独的、非常简单的组件中包含了该部分,如下所示:

const Fields = () => {

     return isSelected.map((isFieldSelected, i) => (
       <TheChildComponent
            key={i}
            isSelected={isFieldSelected}
            onClick={onClick}
       />
     ));

};

这就是我的问题所在。将代码从单独的组件 Fields 移动到父组件的 return 语句时,重新呈现错误消失了。

不过,谢谢你的帮助。