How to memoize a part of a component list in React

4.3k Views Asked by At

Looking for some React rendering/useMemo guidance.

I have a list of 2,000+ items, each of which is a React component. There is a highlight of a "currently selected" item that renders independently of the list. That allows me to render the list only once.

useMemo(_ => {
  <Item .../>
  <Item .../>
  ...
  <Item .../>
}, [contents])

Now I'm adding the ability to "expand" a list item in it, which only adds new DOM elements without modifying the list item in its non-expanded state.

You can imagine each Item being two parts:

function Item(props) {
  return
    <div>
      <SubItem />
      {
        if (props.open) {
          <OtherItem />
        } else {
          null
        }
      }
    </div>
}
useMemo(_ => {
  <Item open={1 == current} .../>
  <Item open={2 == current} .../>
  ...
  <Item open={n == current} .../>
}, [contents, current])

Question: Is there a way to support this without having to re-render the whole list every time I expand/collapse a new item?

Unfortunately the expanded DOM elements are "in flow", so the expansion pushes list items after it (not just a hover/overlay thing).

2

There are 2 best solutions below

2
On

I think you can do it this way:

const Item = useMemo( props => {
    return (
        //...your JSX code here.
    )
}, []);

[...2000].map(props => <Item {...props}>);
0
On

Memoizing the list item alone would not work because the open or openIndex state is in the parent List, not the Item itself.

The only workaround I see is to split the list into chunks of let's say 10 items, and memoize those chunks instead.

You would then use React.memo second parameter arePropsEqual to determine whether the list chunk should re-render, i.e. whether it includes the prevProps.openIndex or nextProps.openIndex.

For a large list like yours, this is well worth the effort.

Check out a demo here - https://stackblitz.com/edit/memoizedlistchunks