4

In my react project I am using react-window package to render nested lists. Each parent FixedSizeList row renders a component which uses another FixedSizeList. Parent List doesn't have more than 14 rows at the moment. But the child List may contain upto 2000 rows. Now my problem is, when I try to scroll through the parent List, all the child list items in the viewport seem to re rendering. This is a little bit problematic for me because in my child list item I am using d3js to draw bar chart with transition effect. So these unnecessary re rendering is giving a overall weird UI. Can anyone help me how can I stop these unnecessary renders.

Here is codesandbox link to a very simple example of my problem. Please open the console log. After initial load the topmost log should be like this: initial console log.

Then if you clear the console and scroll the parent list, you will see log like this: console log after parent scrolling. Here you can see that the child list items of child list 0 is re rendering which is not needed for me.

Can anyone give me a solution that can stop these re rendering?

*P.S. I am not using memo since every row is updating the dom on its own.

Edit

I think this problem would solve if the parent list would stop propagating scroll event to child. I tried to add event.stopPropagation() and event.stopImmediatePropagation() in the parent list row but the output was the same as earlier.

2 Answers2

1

We can use memo to get rid of components being re-rendered unnecessarily for same set of props. And use useCallback to prevent re-creation of a function and thus secure child components being re-rendered. Applying those, we can get this solution:

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>
  );
}
Rakib
  • 170
  • 2
  • 14
0

although the above code works fine, it can be optimized more if we use areEqual method from react-window as react memo dependency. And for more if we want to use other react hooks inside InnerRow component, we must add a middleware component of InnerRow. The full example is given below:

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

Here I am passing data array as useCallBack dependency because I want to re render the InnerRow component if data gets changed.