4

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

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

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

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

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

编辑

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

4

2 回答 2

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>
  );
}
于 2021-08-05T11:54:48.970 回答
0

areEqual虽然上面的代码工作正常,但如果我们使用方法 fromreact-window作为react memo依赖,它可以得到更多的优化。如果我们想在InnerRow组件内部使用其他反应钩子,我们必须添加一个中间件组件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 依赖项传递,因为如果组件发生更改,我想重新渲染InnerRow组件data

于 2021-08-05T14:22:13.580 回答