react-virtualized icon indicating copy to clipboard operation
react-virtualized copied to clipboard

List doesn't update display or row heights when data updated.

Open sAnti09 opened this issue 6 years ago • 12 comments

Setup I have this List that uses CellMeasurer to compute row heights dynamically.

<WindowScroller ref={ref => (this.windowScroller = ref)}>
  {({ height, isScrolling, onChildScroll, scrollTop }) => (
    <AutoSizer disableHeight={true}>
      {({ width }) => (
        <div>
          <List
            autoHeight={true}
            height={height}
            isScrolling={isScrolling}
            onScroll={onChildScroll}
            scrollTop={scrollTop}
            rowCount={contacts.length}
            rowHeight={this._cache.rowHeight}
            rowRenderer={this._rowRenderer}
            width={width}
            deferredMeasurementCache={this._cache}
            overscanRowCount={0}
          />
        </div>
      )}
    </AutoSizer>
  )}
</WindowScroller>

Problem The list doesn't update either display or row heights when data has changed. For the list to change, I have to scroll the window manually. I've tried to clear the cache, updatePosition and forceRender but still has no effect.

Temporary Solution Since scrolling to update isn't acceptable, I created a hack in the componentDidUpdate to be able to update the list when data changes.

/**
   * Since virtual list doesn't update till it scrolls, programmatically scroll window to
   * trigger update.
   */
  public componentDidUpdate(prevProps: Props) {
    const becameVisible = this.props.isVisible && !prevProps.isVisible;
    const contactChanged = this.props.contacts !== prevProps.contacts;

    if (this.windowScroller && (becameVisible || contactChanged)) {
      this._cache.clearAll();
      this.windowScroller!.updatePosition();

      window.scrollTo(window.scrollX, window.scrollY + 5);
      window.scrollTo(window.scrollX, window.scrollY - 5);
    }
  }

sAnti09 avatar Nov 07 '18 04:11 sAnti09

I have the same problem using ReactVirtualized.Grid. I've found that even when using unique keys, the grid display won't update unless the number of rows is different. Example code at https://codesandbox.io/s/3qlqz9o8nq

znarkd avatar Dec 21 '18 23:12 znarkd

I tried ref.forceUpdate() but it only works first time:

this.setState({selectedIndex:index}, ()=>list.forceUpdate())

function({ index, key, style }) { const selected = index===this.state.selectedIndex; return ( <div className={cx("sitcontrol_list_item", selected && "sitcontrol_selected")} data-index={index} key={key} style={style}>{this.props.data[index]}</div> ); }

vjdv avatar Jan 17 '19 21:01 vjdv

I made it work rebinding item renderer before setState:

this.itemRenderer = this.itemRedenrer.bind(this)

Inline arrow function may work too

vjdv avatar Jan 17 '19 21:01 vjdv

You can add ref

       <List
           ref={this.bindListRef}
            autoHeight={true}
            height={height}
            isScrolling={isScrolling}
            onScroll={onChildScroll}
            scrollTop={scrollTop}
            rowCount={contacts.length}
            rowHeight={this._cache.rowHeight}
            rowRenderer={this._rowRenderer}
            width={width}
            deferredMeasurementCache={this._cache}
            overscanRowCount={0}
          />

the bindListRef function

  bindListRef = ref => {
    this.list = ref;
  };

At last, call forceUpdateGrid in ComponentDidUpdate

  componentDidUpdate() {
    if (this.list) {
      this.list.forceUpdateGrid();
    }
  }

superman66 avatar Jan 21 '19 06:01 superman66

@superman66 It works! Thank you for solving my problem.

yuri2peter avatar Mar 29 '19 03:03 yuri2peter

for me this didn't work, so I dynamically set an "artificial" key on the List, and I bump it by 1 to programmatically force a rerender

class MyComponent extends React.Component {
  this.listKey = 0

  componentDidUpdate() { this.listKey += 1 }
  ...

  render() {
    <List key={this.listKey} some more props here />
  }
}

goldylucks avatar Aug 04 '19 14:08 goldylucks

Similar problem here, and when my data updated, row height which should also change doesn't update. Solve this by

componentDidUpdate() {
    if (this.list) {
      this._cache.clearAll(); // re-calculate cache & rowHeight
      this.list.forceUpdateGrid(); 
    }
  }

y2x33 avatar Aug 22 '19 12:08 y2x33

@y2x33 Use react-virtualized's CellMeasurer and pass your element the measure function. From inside the component, when you need to re-measure you can do so for just that one row.

e.g.

  componentDidUpdate(pProps) {
    if (this.props.text !== pProps.text) {
      this.props.measure();
    }
  }

Or you can do this for when dynamic content loads in:

  handleImageLoad = () => {
    // an image has loaded, probably changing the dimensions of this virtualized item!
    this.props.measure();
  }

  render() {
    const { image } = this.props;

    return (
      <div>
        <p>Some other content that shows up first</p>
        <img src={image.src} onload={this.handleImageLoad} />
      </div>
  }

burnhamrobertp avatar Sep 12 '19 13:09 burnhamrobertp

passing in an anonymous or a no-op function () => {} to onRowsRendered prop fixes this problem

iamjayk avatar Dec 05 '19 04:12 iamjayk

Setup I have this List that uses CellMeasurer to compute row heights dynamically.

<WindowScroller ref={ref => (this.windowScroller = ref)}>
  {({ height, isScrolling, onChildScroll, scrollTop }) => (
    <AutoSizer disableHeight={true}>
      {({ width }) => (
        <div>
          <List
            autoHeight={true}
            height={height}
            isScrolling={isScrolling}
            onScroll={onChildScroll}
            scrollTop={scrollTop}
            rowCount={contacts.length}
            rowHeight={this._cache.rowHeight}
            rowRenderer={this._rowRenderer}
            width={width}
            deferredMeasurementCache={this._cache}
            overscanRowCount={0}
          />
        </div>
      )}
    </AutoSizer>
  )}
</WindowScroller>

Problem The list doesn't update either display or row heights when data has changed. For the list to change, I have to scroll the window manually. I've tried to clear the cache, updatePosition and forceRender but still has no effect.

Temporary Solution Since scrolling to update isn't acceptable, I created a hack in the componentDidUpdate to be able to update the list when data changes.

/**
   * Since virtual list doesn't update till it scrolls, programmatically scroll window to
   * trigger update.
   */
  public componentDidUpdate(prevProps: Props) {
    const becameVisible = this.props.isVisible && !prevProps.isVisible;
    const contactChanged = this.props.contacts !== prevProps.contacts;

    if (this.windowScroller && (becameVisible || contactChanged)) {
      this._cache.clearAll();
      this.windowScroller!.updatePosition();

      window.scrollTo(window.scrollX, window.scrollY + 5);
      window.scrollTo(window.scrollX, window.scrollY - 5);
    }
  }

The 5px window scroll helped me a lot! Thanks :smile:

ghost avatar May 12 '21 07:05 ghost

I am facing the same issue. Seems like a very tricky thing for react-virtualized to handle.

mrfambo avatar Jun 06 '21 21:06 mrfambo

Calling the method recomputeRowHeights did the trick for me. The function that was used to calculate height had some state dependencies that was not being applied after an update to the state.

luanraithz avatar Jun 06 '23 17:06 luanraithz