【问题标题】:react-window stop unnecessary rendering of child FixedSizeList rows when scrolling parent FixedSIzeListreact-window 在滚动父 FixedSIzeList 时停止不必要的子 FixedSizeList 行的呈现
【发布时间】:2021-08-04 10:17:57
【问题描述】:

在我的 react 项目中,我使用 react-window 包来呈现嵌套列表。每个父 FixedSizeList 行呈现一个使用另一个 FixedSizeList 的组件。父列表目前不超过 14 行。但子列表最多可能包含 2000 行。现在我的问题是,当我尝试滚动父列表时,视口中的所有子列表项似乎都重新呈现。这对我来说有点问题,因为在我的子列表项中,我使用d3js 来绘制具有过渡效果的条形图。所以这些不必要的重新渲染给了一个整体怪异的 UI。谁能帮助我如何停止这些不必要的渲染。

Here 是我的问题的一个非常简单示例的代码和框链接。 请打开控制台日志。初始加载后,最顶层的日志应该是这样的:initial console log。

然后,如果您清除控制台并滚动父列表,您将看到如下日志:console log after parent scrolling。在这里您可以看到子列表 0 的子列表项正在重新渲染,这对我来说是不需要的。

谁能给我一个可以阻止这些重新渲染的解决方案?

*附注我没有使用备忘录,因为每一行都在自己更新 dom。

编辑

如果父列表停止向子列表传播滚动事件,我认为这个问题会解决。我尝试在父列表行中添加event.stopPropagation() 和event.stopImmediatePropagation(),但输出与之前相同。

【问题讨论】:

    标签: javascript reactjs react-window


    【解决方案1】:

    我们可以使用memo 来摆脱为同一组props 不必要地重新渲染的组件。并使用useCallback 防止重新创建函数,从而确保子组件被重新渲染。应用这些,我们可以得到这个解决方案:

    import "./styles.css";
    import { FixedSizeList as List } from "react-window";
    import { memo, useCallback } from "react";
    
    const Row = memo(({ index: parentIndex, style: parentStyle }) => {
      console.log("rendering child list", parentIndex);
    
      const InnerRow = useCallback(({ index, style }) => {
        console.log("rendering child list item", index, "of parent ", parentIndex);
        return <div style={style}>Child Row {index}</div>;
      }, []);
    
      return (
        <div style={parentStyle}>
          <List height={200} itemCount={1000} itemSize={35} width={270}>
            {InnerRow}
          </List>
        </div>
      );
    });
    
    const Example = () => {
      console.log("rendering parent list");
      return (
        <List height={400} itemCount={16} itemSize={300} width={300}>
          {Row}
        </List>
      );
    };
    
    export default function App() {
      return (
        <div className="App">
          <Example />
        </div>
      );
    }
    

    【讨论】:

      【解决方案2】:

      虽然上面的代码运行良好,但如果我们使用来自react-window 的areEqual 方法作为react memo 依赖项,它可以得到更多优化。如果我们想在InnerRow组件中使用其他react hooks,我们必须添加一个InnerRow的中间件组件。完整示例如下:

        import { FixedSizeList as List, areEqual } from "react-window";
        import { memo, useCallback } from "react";
      
        const Row = memo(({ index: parentIndex, style: parentStyle }) => {
          console.log("mounting child list", parentIndex);
          const data = new Array(15).fill(new Array(500).fill(1));
          const InnerRowCallback = useCallback(
            ({ index, style }) => {
              return <InnerRow index={index} style={style} />;
            },
            [data]
          );
          const InnerRow = ({ index, style }) => {
            console.log("mounting child list item", index, "of parent ", parentIndex);
            return <div style={style}>Child Row {index}</div>;
          };
          return (
            <div style={parentStyle}>
              <List height={200} itemCount={1000} itemSize={35} width={270}>
                {InnerRowCallback}
              </List>
            </div>
          );
        }, areEqual);
        const Example = () => {
          console.log("mounting parent list");
          return (
            <List height={400} itemCount={16} itemSize={300} width={300}>
              {Row}
            </List>
          );
        };
      
        export default function App() {
          return (
            <div className="App">
              <Example />
            </div>
          );
        }
      

      这里我将 data 数组作为 useCallBack 依赖项传递,因为如果 data 发生更改,我想重新渲染 InnerRow 组件。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-02-05
        • 1970-01-01
        • 2013-01-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-20
        相关资源
        最近更新 更多