[英]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.