React Native:更新导航器弹出视图(ListView)
React Native: Update view (ListView) on navigator pop
我是 React Native 的新手,在理解它的整个生态系统方面遇到了一些问题。无论如何,我有一个 ListView 并且用户可以导航到另一个视图以添加将添加到列表中的新项目。当我后退(弹出)时,列表未更新,因为数据源是在 getInitialState 中分配的。
弹出时如何强制View更新列表?这是通过处理导航器的 onDidFocus 中的路线来完成的,还是我可以添加到实际 ListView 中的任何内容?
提供了代码的缩小版本,当导航器出现弹出事件时,预算组件及其列表需要更新。
索引文件
var BudgetWatch_ReactNative = React.createClass({
renderScene(route, navigator){
if(route.name == 'Main'){
return React.createElement(route.component, {navigator});
}
if(route.name == 'Budgets'){
return React.createElement(route.component, {navigator, realm});
}
},
onDidFocus(route){
// Insert React Native magic?
},
render() {
return (
<Navigator
ref={(nav) => { navigator = nav; }}
style={{flex:1}}
initialRoute={{ name: 'Main', component: Main}}
renderScene={this.renderScene}
onDidFocus={this.onDidFocus}
navigationBar={
<Navigator.NavigationBar
routeMapper={NavigationBarRouteMapper(realm)}
/>
}/>
)
}
});
预算构成
class Budgets extends React.Component{
render(){
return (
<View>
<BudgetList navigator = {this.props.navigator}/>
</View>
)
}
}
var BudgetList = React.createClass({
getInitialState: function() {
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
return {
dataSource: ds.cloneWithRows(this.props.realm.objects('Budget')),
};
},
render: function() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
/>
);
}
};
希望这足以让您理解问题,谢谢!
编辑:
试图达到一个函数,该函数将使用来自 onDidFocus 的列表数据设置新状态。但是由于对该函数的调用是通过 route.component.prototype.updateData(data),this.setState returns "Cannot read property 'enqueueSetState' of undefined"
所以我发现这应该是 React Native 中 ListView 渲染的一个错误,通过添加一些不太漂亮的代码,我让它工作了。
我在我的索引文件中添加了一个 onDidFocus 方法,每次显示一个组件时都会调用它(这样当用户弹出到上一个视图时可以使用它)。当 onDidFocus 中的路由为 Budget 时,我再次 return 该组件。我还将 ListView 中使用的数据移出到我的索引中,以便 ListView 将数据作为 属性 获取,并在数据更改时重新呈现:
index.android.js
var BudgetWatch_ReactNative = React.createClass({
renderScene(route, navigator){
if(route.name == 'Main'){
return React.createElement(route.component, {navigator});
}
if(route.name == 'Budgets'){
var data = realm.objects('Budget').sorted('name');
return <Budgets navigator={navigator} realm={realm} data={data} />
}
},
onDidFocus(route){
if(route.name==='Budgets'){
var data = realm.objects('Budget').sorted('name');
return <Budgets navigator={navigator} realm={realm} data={data} />
}
},
render() {
return (
<Navigator
ref={(nav) => { navigator = nav; }}
style={{flex:1}}
initialRoute={{ name: 'Main', component: Main}}
renderScene={this.renderScene}
onDidFocus={this.onDidFocus}
navigationBar={
<Navigator.NavigationBar
routeMapper={NavigationBarRouteMapper(realm)}
/>
}/>
)
}
});
下一步 BudgetComponent.js 我只是将 props 的数据设置为状态,并使用该状态数据将其作为 props 发送到我的 BudgetList 组件(我也将其更改为 class而不是一个组件,但这并不重要):
var Budgets = React.createClass({
getInitialState: function() {
var propsdata = this.props.data;
return {
data: propsdata,
};
},
render(){
return (
<View style={styles.container}>
<BudgetList data={this.props.data} realm={this.props.realm} navigator = {this.props.navigator} />
</View>
)
});
现在这应该可以工作了,但是由于错误,列表中的数据源无法正常工作,因此通过使用方法 componentWillUpdate(在查看列表时一直调用)我可以强制更新数据。但是,为了不陷入死循环,我不得不检查是否应该更新数据,否则会卡在更新数据中:
BudgetList.js
var BudgetList = React.createClass({
getInitialState: function() {
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
var data = this.props.data;
return {
data: this.props.data,
dataSource: ds.cloneWithRows(this.props.data),
};
},
componentWillUpdate (nextProps) {
if (this.state.dataSource._cachedRowCount !== this.props.data.length) {
this.setState({
data: this.props.data,
dataSource: this.state.dataSource.cloneWithRows(this.props.data)
})
}
},
_renderRow: function(rowData: string, sectionID: number, rowID: number) {
return(
<View>
<Text>{rowData.name}</Text>
</View>
)
}
});
我使用 componentDidMount
解决了这个问题,每次出现组件时都会调用它。此解决方案目前对我有效。
例如,
var ListPage = React.createClass({
getInitialState() {
return {
dataSource: ds.cloneWithRows( this._generateRows( this.props.list ) ),
currentPosition: 'unknown'
}
},
componentDidMount() {
this._reloadListView();
},
_reloadListView() {
this.setState({
dataSource: ds.cloneWithRows( this.props.list ),
});
},
_generateRows( list ) {
rows = [];
list.forEach( function( item, index ) {
rows.push( item );
});
return rows
},
_renderRow( rowData ) {
return (
<Text>{ 'id: ' + rowData[ 'id' ] + ' ' + rowData[ 'info' ][ 'value' ][ 'name' ]}</Text>
);
},
render() {
return (
<ListView
dataSource = { this.state.dataSource }
renderRow = { this._renderRow }
/>
)
},
});
module.exports = ListPage;
我是 React Native 的新手,在理解它的整个生态系统方面遇到了一些问题。无论如何,我有一个 ListView 并且用户可以导航到另一个视图以添加将添加到列表中的新项目。当我后退(弹出)时,列表未更新,因为数据源是在 getInitialState 中分配的。
弹出时如何强制View更新列表?这是通过处理导航器的 onDidFocus 中的路线来完成的,还是我可以添加到实际 ListView 中的任何内容?
提供了代码的缩小版本,当导航器出现弹出事件时,预算组件及其列表需要更新。
索引文件
var BudgetWatch_ReactNative = React.createClass({
renderScene(route, navigator){
if(route.name == 'Main'){
return React.createElement(route.component, {navigator});
}
if(route.name == 'Budgets'){
return React.createElement(route.component, {navigator, realm});
}
},
onDidFocus(route){
// Insert React Native magic?
},
render() {
return (
<Navigator
ref={(nav) => { navigator = nav; }}
style={{flex:1}}
initialRoute={{ name: 'Main', component: Main}}
renderScene={this.renderScene}
onDidFocus={this.onDidFocus}
navigationBar={
<Navigator.NavigationBar
routeMapper={NavigationBarRouteMapper(realm)}
/>
}/>
)
}
});
预算构成
class Budgets extends React.Component{
render(){
return (
<View>
<BudgetList navigator = {this.props.navigator}/>
</View>
)
}
}
var BudgetList = React.createClass({
getInitialState: function() {
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
return {
dataSource: ds.cloneWithRows(this.props.realm.objects('Budget')),
};
},
render: function() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
/>
);
}
};
希望这足以让您理解问题,谢谢!
编辑: 试图达到一个函数,该函数将使用来自 onDidFocus 的列表数据设置新状态。但是由于对该函数的调用是通过 route.component.prototype.updateData(data),this.setState returns "Cannot read property 'enqueueSetState' of undefined"
所以我发现这应该是 React Native 中 ListView 渲染的一个错误,通过添加一些不太漂亮的代码,我让它工作了。
我在我的索引文件中添加了一个 onDidFocus 方法,每次显示一个组件时都会调用它(这样当用户弹出到上一个视图时可以使用它)。当 onDidFocus 中的路由为 Budget 时,我再次 return 该组件。我还将 ListView 中使用的数据移出到我的索引中,以便 ListView 将数据作为 属性 获取,并在数据更改时重新呈现:
index.android.js
var BudgetWatch_ReactNative = React.createClass({
renderScene(route, navigator){
if(route.name == 'Main'){
return React.createElement(route.component, {navigator});
}
if(route.name == 'Budgets'){
var data = realm.objects('Budget').sorted('name');
return <Budgets navigator={navigator} realm={realm} data={data} />
}
},
onDidFocus(route){
if(route.name==='Budgets'){
var data = realm.objects('Budget').sorted('name');
return <Budgets navigator={navigator} realm={realm} data={data} />
}
},
render() {
return (
<Navigator
ref={(nav) => { navigator = nav; }}
style={{flex:1}}
initialRoute={{ name: 'Main', component: Main}}
renderScene={this.renderScene}
onDidFocus={this.onDidFocus}
navigationBar={
<Navigator.NavigationBar
routeMapper={NavigationBarRouteMapper(realm)}
/>
}/>
)
}
});
下一步 BudgetComponent.js 我只是将 props 的数据设置为状态,并使用该状态数据将其作为 props 发送到我的 BudgetList 组件(我也将其更改为 class而不是一个组件,但这并不重要):
var Budgets = React.createClass({
getInitialState: function() {
var propsdata = this.props.data;
return {
data: propsdata,
};
},
render(){
return (
<View style={styles.container}>
<BudgetList data={this.props.data} realm={this.props.realm} navigator = {this.props.navigator} />
</View>
)
});
现在这应该可以工作了,但是由于错误,列表中的数据源无法正常工作,因此通过使用方法 componentWillUpdate(在查看列表时一直调用)我可以强制更新数据。但是,为了不陷入死循环,我不得不检查是否应该更新数据,否则会卡在更新数据中:
BudgetList.js
var BudgetList = React.createClass({
getInitialState: function() {
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
var data = this.props.data;
return {
data: this.props.data,
dataSource: ds.cloneWithRows(this.props.data),
};
},
componentWillUpdate (nextProps) {
if (this.state.dataSource._cachedRowCount !== this.props.data.length) {
this.setState({
data: this.props.data,
dataSource: this.state.dataSource.cloneWithRows(this.props.data)
})
}
},
_renderRow: function(rowData: string, sectionID: number, rowID: number) {
return(
<View>
<Text>{rowData.name}</Text>
</View>
)
}
});
我使用 componentDidMount
解决了这个问题,每次出现组件时都会调用它。此解决方案目前对我有效。
例如,
var ListPage = React.createClass({
getInitialState() {
return {
dataSource: ds.cloneWithRows( this._generateRows( this.props.list ) ),
currentPosition: 'unknown'
}
},
componentDidMount() {
this._reloadListView();
},
_reloadListView() {
this.setState({
dataSource: ds.cloneWithRows( this.props.list ),
});
},
_generateRows( list ) {
rows = [];
list.forEach( function( item, index ) {
rows.push( item );
});
return rows
},
_renderRow( rowData ) {
return (
<Text>{ 'id: ' + rowData[ 'id' ] + ' ' + rowData[ 'info' ][ 'value' ][ 'name' ]}</Text>
);
},
render() {
return (
<ListView
dataSource = { this.state.dataSource }
renderRow = { this._renderRow }
/>
)
},
});
module.exports = ListPage;