具有动态高度的反应虚拟化无限滚动问题

Posted

技术标签:

【中文标题】具有动态高度的反应虚拟化无限滚动问题【英文标题】:react-virtualized Infinite scroller issues with dynamic height 【发布时间】:2018-01-18 13:57:08 【问题描述】:

我正在尝试使用 react-virtualized 在我的应用程序中模拟 Facebook 提要,如滚动条。我正在关注here 的参考。我正在尝试一次加载两个提要,然后将调用 loadMoreRows 来获取接下来的两个提要。出于测试目的,我已将 Feed 大小硬编码为 10。它工作良好,直到第 4 次喂食。然后我无法顺利移动。 rowRenderer 一次又一次地触发数字,导致屏幕上的振动效果。如果我以某种方式移动到第 10 个提要并向后滚动,rowRenderer 将再次从 0 开始。我认为这是由于高度不同。与参考类似,我使用CellMeasurerCacheCellMeasurer 来查找动态heightwidth 并将其传递给列表。

class Scroller extends React.Component 
  _cache = new CellMeasurerCache( defaultHeight: 100, fixedWidth: true );
  _resizeAllFlag = false; 
  _mostRecentWidth = 0;
  constructor(props) 
    super(props);
    this.state = 
      localCache: []
    
  
  componentDidMount()
    this._loadData(0); 
  
  componentDidUpdate(prevProps, prevState) 
    console.log(this._list);
    if(this._resizeAllFlag)
      this._resizeAllFlag = false;
      this._cache.clearAll();
      this._recomputeRowHeights();
     else if(this.state.localCache !== prevState.localCache) 
      this._cache.clear(index, 0);
      this._recomputeRowHeights(index);
    
  
  ._loadData = (offset, callback) => 
    //Loads data from server and sets it in this.state.localCache
  
  _recomputeRowHeights = (index) => 
    if (this._list) 
      console.log('Recomputing');
      this._list.recomputeRowHeights(index);
    
  
  _isRowLoaded = ( index ) => 
    return !!this.state.localCache[index];
  
  _loadMoreRows = ( startIndex, stopIndex ) => 
    this._loadData(startIndex, (() => promiseResolver));
    let promiseResolver;
    return new Promise((resolve) => 
      promiseResolver = resolve;
    );
  
  rowRenderer = ( index, key, style, parent ) => 
    const row = this.state.localCache[index];
    let content;
    if (row) 
      content = (<Feed data=row/>);
     else 
      content = (<CustomLoader />);
    
    return (
      <CellMeasurer
        cache=this._cache
        columnIndex=0
        key=key
        parent=parent
        rowIndex=index
        width=this._mostRecentWidth
      > 
        content 
      </CellMeasurer>);
  
  _setListRef = (ref) => 
    this._list = ref;
    this._registerList(ref);
  ;
  _resizeAll = () => 
    this._resizeAllFlag = false;
    this._cache.clearAll();
    if (this._list) 
      this._list.recomputeRowHeights();
    
  ;
  render() 
    const  localCache  = this.state;
    return (
      <div className="flex_grow">
        <InfiniteLoader
          isRowLoaded=this._isRowLoaded
          loadMoreRows=this._loadMoreRows
          rowCount=10
        >
          ( onRowsRendered, registerChild ) =>
            <AutoSizer disableHeight>
              ( width, height ) => 
                if (this._mostRecentWidth && this._mostRecentWidth !== width) 
                  this._resizeAllFlag = true;
                  setTimeout(this._resizeAll, 0);
                
                this._mostRecentWidth = width;
                this._registerList = registerChild;
                return (
                  <List
                    deferredMeasurementCache=this._cache
                    overscanRowCount=1
                    ref=this._setListRef
                    height=height
                    onRowsRendered=onRowsRendered
                    rowCount=10
                    rowHeight=this._cache.rowHeight
                    rowRenderer=this.rowRenderer
                    width=width
                  />
                ) 
              
              
            </AutoSizer>
        </InfiniteLoader>
      </div>
    );
  

更新

我可能已经删除了正在传递的内容中的样式道具。根据@Adrien 的建议,我添加了它。添加样式道具后我的问题没有解决。

rowRenderer = ( index, key, style, parent ) => 
  const row = this.state.localCache[index];
  let content;
  if (row) 
    content = (<Feed style=style data=row/>);
   else 
    content = (<CustomLoader style=style/>);
  
  return (
    <CellMeasurer
      cache=this._cache
      columnIndex=0
      key=key
      parent=parent
      rowIndex=index
      width=this._mostRecentWidth
    > 
      content 
    </CellMeasurer>);
 

还有我的 Feed 组件

class Feed extends React.Component 
  constructor(props) 
    super(props);
  
  render() 
    const  style  = this.props;
    return (
      <div className="flex_grow" style=style>
        /* Feed related JSX */
      </div>
    );
  

我的组件似乎重叠。可能出了什么问题?

答疑者: https://gist.github.com/beb4/cc91f4e9b8982d172613cff248090769

【问题讨论】:

【参考方案1】:

您忘记在 content 组件中传递 rowRenderer 样式。根据the docs 的说法,这种样式是强制定位行的。

更正行渲染器

rowRenderer = ( index, key, style, parent ) => 
    const row = this.state.localCache[index];
    let content;
    if (row) 
      content = (<Feed data=row style=style />); // <== HERE
     else 
      content = (<CustomLoader style=style />); // <== AND HERE
    
    return (
      <CellMeasurer
        cache=this._cache
        columnIndex=0
        key=key
        parent=parent
        rowIndex=index
        width=this._mostRecentWidth
      > 
        content 
      </CellMeasurer>
    );

【讨论】:

感谢您的帮助。我最初有风格。不知何故,在编辑时我可能已经删除了它。我按照你的建议再次添加。即使添加了style props,我也遇到了同样的问题。 @user2193672 你是否在Feed 组件内的根节点上应用style 属性?你能粘贴你的代码吗? 是的。我将 style 道具传递给了我的 FeedCustomLoader ,而不是按照您的建议传递给 CellMeasurer 。已经用它更新了问题。 @user2193672 我的意思是应用style 不仅仅是传递style 属性,而是在Feed 组件中处理它。你能粘贴你的Feed 组件吗? (仅渲染) 让我们continue this discussion in chat.

以上是关于具有动态高度的反应虚拟化无限滚动问题的主要内容,如果未能解决你的问题,请参考以下文章

如何进行无限滚动,在Vue js中动态渲染列表(仅可见)

列表无限滚动(虚拟列表)

为啥通过 AJAX 无限滚动加载的内容的高度在加载后没有正确测量?

无限滚动时防止滚动条跳跃

javascript 反应无限滚动装饰组件

JQ-滚动条下拉无限的加载数据