react-window停止在滚动父FixedSizeList时不必要地渲染子FixedSizeList行



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

这里是codesandbox链接到我的问题的一个非常简单的例子。请打开控制台日志。初始加载后,最上面的日志应该是这样的:

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

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

*注:我没有使用memo,因为每一行都在自己更新dom。

编辑

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

我们可以使用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>
);
}

虽然上面的代码工作得很好,但如果我们使用react-windowareEqual方法作为react memo依赖项,它可以得到更多的优化。如果我们想在InnerRow组件中使用其他react钩子,我们必须添加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得到改变。

最新更新