繁体   English   中英

React Native - 无限滚动ListView

[英]React Native - Infinite Scroll ListView

我试图在React Native中实现无限滚动ListView。 这通过检测列表视图何时到达其内容长度的底部并触发另一个API来工作。 收到响应后,新数据将附加到原始数据(这在我的Flux存储中完成)。 但是在我的组件中,我通过以下方式更新dataSource:

setState({
    dataSource: this.state.dataSource.cloneWithRows(Store.getLatestData())
})

我似乎遇到的问题是每次发生这种情况时,ListView都会刷新并丢失其滚动位置(它返回到顶部)。 如何在不丢失当前位置的情况下添加新数据?

谢谢!

UPDATE

我想在添加新数据之前保存滚动偏移,然后在添加数据后滚动到原点偏移。 我试图这样做:

this._listView.getScrollResponder().scrollTo(offset, 0);

其中this._listView是ListView组件的引用。 但是,每当我访问它时,this._listView似乎总是为null。 我的猜测是,在重新渲染过程中,对listview的引用为零秒?

更新2

欲获得更多信息:

我的getInitialState是:

getInitialState: function() {
    var ds = new ListView.DataSource({
        rowHasChanged: (r1, r2) => r1 !== r2,
        sectionHeaderHasChanged: (s1, s2) => true
    });
    return {
        dataSource: ds.cloneWithRows(LeaderboardsStore.getLeaderboard()),
        //some other state too
    }

一旦我检测到用户已滚动到底部,我就通过fetch API启动API调用,该API返回新数据并通过concat附加到原始数组。 保存阵列的存储(磁通存储)会发出更改事件。

在我的onStoreChange回调中,我重新分配dataSource,如下所示:

setState({
    dataSource: this.state.dataSource.cloneWithRows(Store.getLatestData())
})

它按预期更新我的ListView(滚动位置的不必要的更改)。

我的渲染方法看起来像(为简单起见):

render:function() {
    return (
        <ListView 
            dataSource={this.state.dataSource}
            renderRow={this._renderRow}
            ref={(component) => {this._listView = component;}}
         />
    );
}

我尝试将onStoreChange回调更改为:

onStoreChangeCallback: function() {
    this._scrollOffset = this._listView.scrollProperties.offset;
    this.setState({
        dataSource: this.state.dataSource.cloneWithRows(Store.getLatestData())
    }, function() {
        if (this._scrollOffset !== undefined) {
            this._listView.getScrollResponder().scroll(offset, 0);
        }
    });
}

但是,this._listView似乎在getScrollResponder行为null :(

在没有看到更多代码的情况下知道问题有点困难,但是,我假设React Native不理解您已将项目合并到原始列表中。 确保使用concat加入项目:

incidents = _.uniq(incidents.concat(payload.action.response), "id") IncidentsStore.emitChange(RECEIVED_EVENT)

在这个例子中,我使用concat然后lodash以确保我没有偶然的双打。

另外,你的getLatestData应该返回原始项的副本( slice(0)是一种简单的方法),如下所示:

getIncidents: (state) ->
  dataSource: state.dataSource.cloneWithRows(incidents.slice(0))
  loaded: true

希望有所帮助,如果您有疑问,请告诉我。

UPDATE

您无需保存位置。 您的列表视图应如下所示:

<ListView
  dataSource={this.state.dataSource}
  renderRow={this.renderRow}
  style={styles.listView}
  onEndReachedThreshold=1200
  onEndReached={this.fetchMoreLeaders}
/>

在我的组件中,我添加以下侦听器:

componentDidMount: function() {
  this.pageNumber = 0;
  IncidentsStore.addLeadersReceivedListener(this.updateList);
  return this.fetchMoreLeaders();
},

fetchMoreLeaders: function() {
  return LeadersActions.fetchLeaders(this.pageNumber += 1);
},

updateList: function() {
  return this.setState(LeadersStore.getLeaders(this.state));
},

我的商店将返回以下内容:

getLeaders: function(state) {
  return {
    dataSource: state.dataSource.cloneWithRows(leaders.slice(0)),
    loaded: true
  };
},

当新数据出现时,我是我之前用_.uniq写的concat

我不确定你是否能够解决这个问题,但我遇到了同样的问题而且我正在添加适合我的方法。

首先将数据设置为状态,或者您可以使用getInitialState

state = {
    loading: true,
    currentPage: 1,
    data: {
      collection: []
    }
  }

然后在componentDidMount获取所需的数据,这里user是我的服务,它只是进行api调用的包装器。

componentDidMount() {
    this.setState({loading: true}, ()=>{
        user.artists().then((data)=>{
          const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
          const dataSource = ds.cloneWithRows(data.collection);
          this.setState({
            data,
            dataSource,
            loading: false
          });
          console.log(data.last_page);
        }).catch((err)=>{
          console.log(err);
        }).done();
      });
  }

这就是我渲染ListView

renderPartial(datas) {
    if (datas.length === 0) {
      return <View/>;
    }
    const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    const dataSource = ds.cloneWithRows(datas);
    return (
      <ListView
        enableEmptySections={true}
        dataSource={dataSource}
        onEndReached={()=>this.loadOtherPage()}
        onEndReachedThreshold={400}
        renderRow={(rowData) => this.renderContent(rowData)}
      />
    );
  }

onEndReached触发this.loadOtherPage函数,就像这样,在这里你可以看到我只是将数据推data.collection.push(item); 这样,新数据与现有数据连接,视口保持在当前位置,而不是返回到顶部。

loadOtherPage() {
    if (this.state.currentPage !== this.state.data.last_page && !this.state.loading) {
      this.setState({currentPage: this.state.currentPage + 1, loading: true}, ()=>{
        user.artists({page: this.state.currentPage})
          .then((response)=>{
            const data = this.state.data;
            response.collection.map((item)=>{
              data.collection.push(item);
            });
            this.setState({
              data,
              loading: false});
          }).catch((err)=>{
            console.log(err);
          });
      });
    } else {
      console.log('max page reached or it is still loading');
    }
  }

我希望这可以帮助别人。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM