我正在尝试在React Native中实现无限滚动ListView。这通过检测列表视图何时到达其内容长度的底部并触发另一个API来工作。收到响应后,新数据将附加到原始数据(这在我的Flux存储中完成)。但是在我的组件中,我通过以下方式更新了dataSource:
setState({
dataSource: this.state.dataSource.cloneWithRows(Store.getLatestData())
})
我似乎遇到的问题是每次发生这种情况时,ListView都会刷新并丢失其滚动位置(它返回到顶部)。如何在不丢失当前位置的情况下添加新数据?
谢谢!
更新
我想在添加新数据之前保存滚动偏移,然后在添加数据后滚动到原点偏移。 我试图这样做:
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:(
答案 0 :(得分:0)
在没有看到更多代码的情况下知道这个问题有点难,但是,我假设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
希望有所帮助,如果您有疑问,请告诉我。
<强>更新强>
您不需要保存位置。您的列表视图应如下所示:
<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答案 1 :(得分:0)
我不确定你是否能够解决这个问题,但我遇到了同样的问题而且我正在添加适合我的方法。
首先将数据设置为状态,或者您可以使用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');
}
}
我希望这有助于某人。