bvaughn / react-window

React components for efficiently rendering large lists and tabular data
https://react-window.now.sh/
MIT License
15.54k stars 779 forks source link

How to update itemSize when items changes? I'm getting gaps when filtering items #707

Open paulwongx opened 1 year ago

paulwongx commented 1 year ago

I'm rendering a sidebar that can be filtered down every time someone types into the searchbar. A simplified version of my code is below. Everything works except for the fact that there are gaps when filtering. Keep in mind, each section is a conventional "Row" and not a link. Essentially, say the first section gets filtered out, the Row components renders the new first Row properly, but the OLD height is being used for that new first row.

How can I update the itemSize once the list changes?

From reading other issues, I think this prop might help resetAfterIndex but how are you supposed to use it? resetAfterIndex={0} doesn't work and neither does resetAfterIndex={{index:0}}. It says no overload matches this call. But the typescript definitions are there. I think this is only there in class components or something... cuz it looks like the typedefs are for classes...?

const Row = memo(function Row({
  index,
  style,
  data: { navigation },
}: ListChildComponentProps<{
  navigation: Navigation[];
}>) {
  const nav = navigation[index];

  return (
    <li key={nav.name} style={style}>
      <h3>{nav.name}</h3>
      {nav.children.map((child) => (
          <Link key={child.name} href="#">
            {child.name}
          </Link>
        ))}
    </li>
  );
}, areEqual);

const getItemHeight = ({ navSection }: { navSection: Navigation }) => {
  return 36 + navSection.children.length * 36;
};

function List() {
  const { navigation } = useContext(NavigationContext);

  // This useCallback isn't working as intended... It should update the itemSize whenever navigation changes
  const getItemSize = useCallback((index:number)=> getItemHeight({ navSection: navigation[index] }), [navigation]);
  const getItemLength = useMemo(()=> navigation.length, [navigation]);

  return (
    <AutoSizer>
      {({ height, width }: Size) => (
        <VariableSizeList
          className="scrollbar"
          height={height}
          itemCount={getItemLength}
          itemSize={getItemSize} // <----- How can I update this dynamically whenever navigation changes? useCallback isn't working. 
          width={width}
          itemData={{ navigation }}
        >
          {SidebarItem}
        </VariableSizeList>
      )}
    </AutoSizer>
  );
}

image

nolatone commented 1 year ago

I'm curious about this as well. Hopefully this will be assigned soon.

xerosanyam commented 1 year ago

+1

xerosanyam commented 1 year ago

this solved the issue for me.

useEffect(() => {
    console.log("re-compute styles");
    varRef.current?.resetAfterIndex(0);
  }, [finalList]);
piczi commented 1 year ago

@xerosanyam thank you useEffect(() => { !!dataSource?.length && instanceRef?.current?.resetAfterIndex?.(0); }, [dataSource]);

timminata commented 1 year ago

@xerosanyam Thanks, it seems like this can actually trigger an update. Any idea why forceUpdate() does not fix it? I assumed forceUpdate() would do the trick, but it does not.

deviun commented 11 months ago

+1