bvaughn / react-window

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

Scrolling up in VariableSizeList causes major scroll jitters when using scrollToItem. #741

Open shaunchander opened 9 months ago

shaunchander commented 9 months ago

If you create a VariableSizeList and scroll to a certain element in it using listRef.current.scrollToItem(200); then attempt to scroll up, there is a very visible jitter. I believe this happens because components of dynamic height are rendering and being upserted into the DOM above where the user is currently positioned.

The components I'm working with a dynamically sized, unlike into the demo on the docs where each element in the variable list has its heighted pre-calculated.

I'll create a minimum-reproducible-demo shortly!

niemal commented 9 months ago

Very interesting, are you using a shadow-DOM like technique with opacity: 0 to calculate everything? I am very curious into this, thanks.

niemal commented 9 months ago

EDIT: The major clutter is gone (before I had messages flying around and incorrect persistent heights for some reason), now there is a tiny bit of clutter for a few milliseconds, I don't know if my approach is the most ideal to begin with. Looking forward to your solution.

The clutter is mostly gone doing what I mentioned above:

Just above my list component:

        {!heightsCalculated && (
          <ShadowHeightCalculator messages={localMessages} />
        )}

In my ListItem component I am running the setRowHeight function in a useEffect, ShadowHeightCalculator component looks like:


function ShadowHeightCalculator({
  messages,
}: {
  messages: Array<MyDataType>;
}) {
  return (
    <div style={{ position: "relative", opacity: 0, height: "1px" }}>
      {messages.map((message, idx) => (
        <ListItem
          key={`shadow-height-${idx}`}
          index={idx}
          style={{
            position: "absolute",
            left: 0,
            top: 0,
          }}
        />
      ))}
    </div>
  );
}

And I am running a loop somewhere (a useInterval hook) checking it like so:

   const checkHeightsCalculated = (messages: Array<MyDataType>) => {
      const heightsCalculated = messages.some(
        (msg) =>
          rowHeights?.current &&
          typeof rowHeights.current[msg.hash ?? ""] !== "number"
      );

      return !heightsCalculated;
  };