ReactJS 如何在循环中记忆以呈现相同的组件

ReactJS how to memoize within a loop to render the same component

我有一个使用循环创建多个组件的组件,但我只需要重新渲染正在修改的实例,而不是其余的实例。这是我的方法:

    function renderName(item) {
       return (
          <TextField value={item.value || ''} onChange={edit(item.id)} />
       );
    }
    
    function renderAllNames(items) {
       const renderedItems = [];
       items.forEach(x => {
          const item = React.useMemo(() => renderName(x), [x]);
          renderedItems.push(item);
       });
       return renderedItems;
    };
    
    return (
      <>
         {'Items'}
         {renderAllNames(names)};
      </>
    );

这告诉我 hooks 调用比之前的渲染要多。尝试这样做:

function renderAllNames(items) {
           const renderedItems = [];
           items.forEach(x => {
              const item = React.memo(renderName(x), (prev, next) => (prev.x === next.x));
              renderedItems.push(item);
           });
           return renderedItems;
        };

也没用...基本方法很好用

function renderAllNames(items) {
           const renderedItems = [];
           items.forEach(x => {
              renderedItems.push(renderName(x));
           });
           return renderedItems;
        };

但它会在我每次编辑任何字段时呈现所有动态组件,那么我如何才能记住它以便仅重新呈现正在编辑的项目?

您正在破坏 rules of hooks。 Hooks 应该只用在组件的顶层,这样 React 才能保证调用顺序。组件记忆也应该只使用 React.memo 完成,并且组件应该只在全局范围内声明,而不是在其他组件内声明。

我们可以将 renderName 变成它自己的组件,RenderName:

function RenderName({item, edit}) {
    return (
      <TextField value={item.value || ''} onChange={() => edit(item.id)} />
   );
}

然后这样记下来:

const MemoRenderName = React.memo(RenderName, (prev, next) => {
  const idEqual = prev.item.id === next.item.id;
  const valEqual = prev.item.value === next.item.value;
  const editEqual = prev.edit === next.edit;

  return idEqual && valEqual && editEqual;
});

React.memo默认对所有props进行严格比较。由于 item 是一个对象,没有两个对象是严格相等的,所以必须深入比较属性。旁注:这仅在 edit 是引用稳定函数时才有效。你没有展示它,但它必须被包裹在它自己的记忆挂钩中,例如 useCallback 或者完全脱离渲染周期。

现在回到父组件你可以直接映射names:

return (
  <>
     {'Items'}
     {names.map(name => <MemoRenderName item={name} edit={edit}/>)}
  </>
);