Skip to content

Problem refreshing while filtering datas #1104

Closed as not planned
Closed as not planned

Description

@basile-parent

Hello,
I have a problem using the Grid component. I doing an iTunes like software and I'm trying to implement a search function.
image

The datas are well filtered, the props are updated in all components but the rendering is very strange. If the result of the filtering return 3 rows (like in the below image), the grid render the first 3 rows of the initial data set and not taking into account the new data set.
image

My code :

class TableContainer extends React.Component {

    render() {
        let filteredrowRenderers = [];
        if (this.state.rowRenderers) {
            filteredrowRenderers = this._getFilteredData();
        }

        return (
            <section id="list">
                <section id="searchData">
                    <FontIcon className="material-icons">search</FontIcon>
                    <TextField className="textField" name={"search"} placeholder={"Recherche"}
                               onKeyPress={ e => this._searchData(e.target.value) }
                    />
                </section>
                <MyVirtualizeTable headers={ this.headers }
                                 data={ filteredrowRenderers }/>
            </section>
        );
    }

    _getFilteredData() {
        const {rowRenderers, searchText} = this.state;

        if (searchText) {
            return rowRenderers.filter(rowRenderer => rowRenderer.data.searchText.indexOf(searchText.toLowerCase()) >= 0);
        }
        return rowRenderers;
    }

    _searchData(text) {
        this.setState({
            ...this.state,
            searchText: text
        });
    }
    
}

export default class MyVirtualizeTable extends React.PureComponent {

  ...

  render() {
    const rowCount = this.props.data.length;

    return (
      <div>
        <ScrollSync>
          {({
              clientHeight,
              clientWidth,
              onScroll,
              scrollHeight,
              scrollLeft,
              scrollTop,
              scrollWidth,
            }) => {
            return (
              <div ...>
                <AutoSizer>
                  {(childrenParam) => (
                    <div>
                      <div
                        <Grid
                          className={styles.HeaderGrid}
                          columnWidth={(index) => this._getColumnWidth(index, childrenParam.width - scrollbarSize())}
                          columnCount={columnCount}
                          height={rowHeight}
                          overscanColumnCount={overscanColumnCount}
                          cellRenderer={(headerParam) => this._renderHeaderCell(headerParam, childrenParam.width - scrollbarSize())}
                          rowHeight={rowHeight}
                          rowCount={1}
                          scrollLeft={scrollLeft}
                          width={childrenParam.width - scrollbarSize()}
                        />
                      </div>
                      <div>
                        <Grid
                          cellRenderer={(headerParam) => this._renderBodyCell(headerParam, childrenParam.width - scrollbarSize())}
                          
                          className={styles.BodyGrid}
                          columnWidth={(index) => this._getColumnWidth(index, childrenParam.width - scrollbarSize())}
                          columnCount={columnCount}
                          height={childrenParam.height}
                          onScroll={onScroll}
                          overscanColumnCount={overscanColumnCount}
                          overscanRowCount={overscanRowCount}
                          rowHeight={rowHeight}
                          rowCount={rowCount}
                          width={childrenParam.width}
                        />
                      </div>
                    </div>
                  )}
                </AutoSizer>
              </div>
            );
          }}
        </ScrollSync>
      </div>
    );
  }

  _renderBodyCell({columnIndex, key, rowIndex, style}, totalWidth) {
    const rowRenderer = this.props.data[rowIndex];
    
    return (
      <div key={"body_" + key}>
        { rowRenderer.renderCell(columnIndex) }
      </div>
    );
  }

  _renderHeaderCell({columnIndex, key, rowIndex, style}, totalWidth) {
    const headerData = this.props.headers[columnIndex];

    return (
      <div key={"header_" + key}>
        { headerData.name }
      </div>
    );
  }
  
  ...

}
export class RowRenderer {

	...
    
  renderCell = (column) => {
    
    switch (column) {
      case 0 :
        return (
          <div>
            { this.data.propA }
          </div>
        );
      case 1 :
        return (
          <div>
            { this.data.propB }
          </div>
        );
      case 2 :
        return (
          <div>
            { this.data.propC }
          </div>
        );
      ...
  };
  
}

Do you have any clue on why the grid is rendering its initial datas filtered on the n first rows an not the filtered datas its receives with props ?

Thank you.

Activity

  1. changed the title [-]Problem resfreshing while filtering datas[/-] [+]Problem refreshing while filtering datas[/+] on May 12, 2018
  2. pete-moss commented on May 18, 2018

    @pete-moss

    In our use, if our data changes, we sometimes have to call forceUpdate() on the Grid instance to make it re-render. I think there are some optimizations in the Grid code to minimize the calls to render(). Simply reducing the rowCount may not be enough to trigger a rerender. This is even worse if you are using variable-height rows where you may have to call Grid.recomputeGridSize() after a data change.

  3. Anderson-James-oss commented on May 24, 2018

    @Anderson-James-oss

    How to use this scenario ? (forceUpdate())

    I want to ask when to call forceUpdate() ? How to use it ?
    I'm not going to use forceUpdate()

    export default class VirtTable extends React.Component {
        constructor(props){
            super(props);
        }
        
        _cellRenderer = ({columnIndex, key, rowIndex, style}) => {
           return (
               <div>{this.props.option.dataSource[rowIndex][columnIndex]}</div>
           );
         }
        
        componentDidUpdate(){
            //Do you use it here   
            // ....this.forceUpdate()
          }
    
        render(){
            return(
               <AutoSizer>
               {({ height, width }) => (
                     <MultiGrid
                     cellRenderer={this._cellRenderer}
                     columnWidth={146}
                     columnCount={this.props.option.dataSource[0].length}
                     fixedColumnCount={1}
                     fixedRowCount={0}
                     height={height}
                     rowHeight={60}
                     rowCount={this.props.option.dataSource.length}
                     width={width}
                     style={STYLE}
                     styleBottomLeftGrid={STYLE_BOTTOM_LEFT_GRID}
                     styleTopLeftGrid={STYLE_TOP_LEFT_GRID}
                     styleTopRightGrid={STYLE_TOP_RIGHT_GRID}
                   />
                   )}
               </AutoSizer>
            )
        }
    }
    
  4. pete-moss commented on May 24, 2018

    @pete-moss

    In our component that wraps a MultiGrid (equivalent to your VirtTable), we added a dataFreshness: any prop. If you do that, the user of VirtTable would then change this value whenever the data changes. Then in the componentWillReceiveProps() lifecycle method, check to see if dataFreshness has changed. If so, call MultiGrid.forceUpdateGrids(). You will have to get a ref to the MultiGrid in order to call this method. That forces a rerender pass on all Grids contained by the MultiGrid. That should ensure that your new rows are rendered.
    This technique works great when we are fetching new data and doing sorting and filtering since the rowCount may or may not change.

  5. wuweiweiwu commented on Jun 1, 2018

    @wuweiweiwu
    Contributor

    I would try to update to 9.19.x version of react virtualized. any prop changes should trigger a re-render.

    however, forceUpdate and forceUpdateGrids are also viable alternatives.

    hope that helps!

  6. wuweiweiwu commented on Jun 1, 2018

    @wuweiweiwu
    Contributor

    @koalajun I would recommend calling forceUpdate when you data updates.

    ex:

    fetch('/endpoint')
      .then(data => data.json())
      .then(data => {
         this.setState({ gridProps: data }, () => this.gridRef.forceUpdate());
       })
  7. kpennell commented on Aug 16, 2018

    @kpennell

    Having similar problems with client side filtering. All of this has forced me to learn this api well. But yeah, still can't figure out how to update this.

  8. kpennell commented on Aug 17, 2018

    @kpennell

    @krayush thx for this. I'll give it a try.

  9. priley86 commented on Jun 21, 2019

    @priley86

    Hi, I'm seeing a similar issue when data changes after I have set isScrollingOptOut = true. My current workaround is to refresh the grid onComponentUpdate:

    https://github.com/openshift/console/blob/5107eacc97430f453dc9cf573d0f779dd5eea746/frontend/public/components/factory/table.tsx#L365

    We've written a wrapper around VirtualGrid due to changes proposed in #1377, however this is essentially the same:

     this._cellMeasurementCache.clearAll();
     this._gridRef.recomputeGridSize();
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions