【问题标题】:ReactJS how to memoize within a loop to render the same componentReactJS如何在循环中记忆以呈现相同的组件
【发布时间】:2021-05-29 20:56:24
【问题描述】:

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

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

这让我大喊,钩子调用比之前的渲染要多。改为尝试:

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

但是每次我编辑任何字段时它都会渲染所有动态组件,那么我怎样才能记住这个以便仅重新渲染正在编辑的项目?

【问题讨论】:

  • 能否提供组件的完整代码?

标签: reactjs react-hooks memoization


【解决方案1】:

你破坏了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}/>)}
  </>
);

【讨论】:

  • 嘿,我想在这里添加评论,请原谅:) 匿名功能的编辑道具可能无法通过您的平等检查。因此,您应该使用 useCallback 记住这一点,或者通过命名 thje 函数或将其设置为变量来使其知道。在备忘录的自定义相等函数中,您可以从 Lodash 传递一个普通的 isEqual 函数。在我的操作中也可以提高可读性。
  • @CaioKoiti 我的回答假设 edit 是 OPs 帖子中的命名函数。没有理由认为它在这里是匿名的,我在回答中确实说过useCallback。我不同意在这里使用深度平等检查器。在尝试提高性能时,您应该小心使用更昂贵的算法。一个更好的主意是将对象中的所有值分解为它们自己的道具,这样您根本不需要自定义检查。我在这里没有这样做,因为这篇文章的目的是阐明 React.memo 的工作原理。
猜你喜欢
  • 1970-01-01
  • 2020-07-20
  • 2022-01-22
  • 2020-09-01
  • 2018-03-26
  • 1970-01-01
  • 1970-01-01
  • 2018-07-15
  • 2021-11-19
相关资源
最近更新 更多