【问题标题】:Reducing React table rerendering减少 React 表重新渲染
【发布时间】:2021-02-18 18:24:13
【问题描述】:

我有一个包含两列的表格,每一列都是可编辑的。该表还包括添加新条目的功能。我遇到的问题是,每当编辑单行时,都会重新渲染每一行。对于较大的表,这会在编辑单行时导致明显的击键延迟。关于如何防止这种情况的任何想法?

我无法在“小”示例中复制性能问题,但https://codesandbox.io/s/zen-williams-r8pii?file=/src/App.js 是基本功能。我尝试在几个地方删除 React.memo 无济于事(我是这方面的新手)

感谢您的帮助!

【问题讨论】:

    标签: reactjs optimization rendering


    【解决方案1】:

    有一些事情导致行重新呈现:

    首先需要将行组件包裹在React.memo

    const ThingRow = React.memo(({ thing, onUpdate }) => {
       ...
    })
    

    那么onUpdate需要和React.useCallback一起momoized,为了减少useCallback中的依赖到只有setThings(见注),你可以使用setThings.map的回调版本更新项目

    const ListOfThings = ({ things, setThings }) => {
      const onUpdate = React.useCallback(
        (thing) => {
          setThings((prevThings) =>
            prevThings.map((t) => (t.id === thing.id ? thing : t))
          );
        },
        [setThings]
      );
      return (
        <table>
          <tbody>
            {things.map((thing) => (
              <ThingRow key={thing.id} thing={thing} onUpdate={onUpdate} />
            ))}
          </tbody>
        </table>
      );
    };
    

    当遇到此类问题时,您需要寻找方法来记忆您传递的道具,特别是回调并减少挂钩中的依赖关系

    这是codeandbox fork https://codesandbox.io/s/table-row-memoization-xs1d2?file=/src/App.js

    注意:根据react docs

    React 保证 setState 函数的身份是稳定的,并且不会在重新渲染时改变

    【讨论】:

      【解决方案2】:

      您可以尝试一些优化。 首先,如果您要处理大量数据,核心问题可能是您向 DOM 渲染了太多东西。 如果是这种情况,我会先在表格中添加虚拟化或分页。 您可以使用库轻松添加虚拟化,例如使用来自react-window 的 FixedSizeList:

      import { FixedSizeList as List } from 'react-window';
       
      const Row = ({ index, style }) => (
        <div style={style}>Row {index}</div>
      );
       
      const Example = () => (
        <List
          height={150}
          itemCount={1000}
          itemSize={35}
          width={300}
        >
          {Row}
        </List>
      );
      

      这应该已经减少了任何性能问题,但如果您只想优化重新渲染: react 的默认行为是渲染调用 useState 设置器的当前组件以及所有子组件,即使没有任何 props 更改。 因此,您应该将 ListOfThings 和 ThingRow 都包含在备忘录中以选择退出此行为:

      const ListOfThings = memo({ things, setThings }) => {
         ...
      });
      const ThingRow = memo(({ thing, onUpdate }) => {
         ...
      });
      

      如果不这样做,更改不相关的标题输入将导致重新渲染所有内容。

      最后,您将一个内联事件处理程序 (onUpdate) 传递给 ThingRow。 这将为 ListOfThings 的每次渲染创建一个新的函数对象,这将绕过我们之前所做的备忘录优化。 为了优化这一点,您可以使用 useCallback 创建对该函数的持久引用:

      onUpdate={useCallback(
        thing => setThings(prev => prev.map(t => t.id === thing.id ? thing : t)),
        [setThings])}
      

      请注意,当您依赖先前的更新值时,最好在 useState 中使用函数更新样式。

      小免责声明:如果您遇到性能问题,我只会使用虚拟化/分页解决方案,而不会尝试针对重新渲染进行优化。 React 的默认渲染行为非常快,更改它可能会引入很难找到与未更新内容相关的错误。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-12-29
        • 1970-01-01
        • 2016-12-30
        • 1970-01-01
        • 2010-10-27
        • 2021-06-03
        • 2020-08-31
        • 2021-12-27
        相关资源
        最近更新 更多