4

Inside of an InfiniteLoader I have a List, whose rowRenderer callback creates a <div> wrapped by CellMeasurer (I'm also using a cache for that). The rows can vary wildly in size, but this setup is working well for scrolling purposes.

However, elements within each row can dynamically change in height based on user input. In those cases, I call:

cellMeasurerCache.clear(rowIndex, 0);
this._listRef.recomputeRowHeights(rowIndex);

But in some cases, this causes the rows to jump around -- this is because the top values are getting calculated incorrectly. For example, I'm displaying a document with the following rows rendered in List:

rowIdx        height       top
  3            668px       420px
  4           8547        1088
  5           2420        9635

Then the height of row 4 increases slightly (~50px). After running the above code with rowIndex == 4, I see the following rows:

rowIdx        height       top
  5           2420px      3088px
  6           1156        5508
  7           4718        6664
  8           1050       11382

As you can see, row 5 now has an incorrect top value, causing the rows to jump around. I'm assuming that the height of row 4 did not get measured correctly.

What's wrong?


Update 2017-11-08: It appears that calling cellMeasurerCache.clear(rowIndex, 0) replaces the height of that row with the value of estimatedRowSize, which is very different. It doesn't seem to re-measure, contrary to what the docs say.

CellMeasurer._maybeMeasureCell() isn't called for row 4 after it gets the default height. My rowRenderer callback (which creates CellMeasurer around my content) is only invoked starting at row 5. So the code has made the decision of which rows to show with row 4 having the much smaller default height, instead of calculating beforehand.

  • "It appears that calling `cellMeasurerCache.clear(rowIndex, 0)` replaces the height of that row with the value of `estimatedRowSize`, which is very different. It doesn't seem to re-measure, contrary to what the docs say." This is correct. This method clears the cached measurement so that it will be re-measured the next time the cell is rendered. The subsequent call to `list.recomputeRowHeights` will re-render, which triggers the measurement. At least that's how it's supposed to work. If that is not happening, it sounds like maybe there's a bug. – bvaughn Nov 09 '17 at 11:47
  • Right, I was not seeing that. Where should it invoke the `CellMeasurer` when calling `list.recomputeRowHeights`? When stepping through the code, I only ever saw it used the default value. – Javier Pedemonte Nov 09 '17 at 15:39
  • I wrote this app as a proof of concept a while ago: tweets.now.sh – bvaughn Nov 09 '17 at 21:32
  • The main source code is here: https://github.com/bvaughn/tweets/blob/master/src/components/TweetList.js – bvaughn Nov 09 '17 at 21:33
  • Maybe it's helpful for you? – bvaughn Nov 09 '17 at 21:33
  • Yep, I saw that code (tweets.now.sh doesn't load for me -- redirects to zeit.co). What I'm seeing is similar to https://github.com/bvaughn/react-virtualized/issues/853. – Javier Pedemonte Nov 10 '17 at 13:08
  • (Hm. Loads fine for me. Maybe Zeit was having issues?) – bvaughn Nov 10 '17 at 15:54

1 Answers1

3

Not sure if this is the correct/best way to handle this, but I was able to get this working like so:

First, I used the callback version of CellMeasurer, storing the measure callback for later use (stored per row):

rowRenderer = ({ key, index, style, isScrolling, parent }) => {
  const getContent = measure => {
    // store callback for later use
    this._measureCallbacks[index] = measure;

    // ... get your content ...
    return content;
  };

  return (
    <CellMeasurer
      key={key}
      cache={cellMeasurerCache}
      columnIndex={0}
      parent={parent}
      rowIndex={index}
    >
      {({ measure }) => (
        <div key={key} style={style}>
          {getContent(measure)}
        </div>
      )}
    </CellMeasurer>
  );
};

Later, when a row height changes, instead of calling cellMeasurerCache.clear(), I run:

// tell row to re-measure
this._measureCallbacks[rowIndex]();
// tell list to recompute row offsets
this._listRef.recomputeRowHeights(minRow);
  • 2
    can you please help me? i have the same problem. What is this._measureCallbacks? can you please explain it? – YTG Apr 22 '21 at 09:22