Reputation: 1333
I'm pretty new to React Native and having some problem with understanding the whole ecosystem of it. Anyway, I have a ListView and the user can navigate to another View in order to add a new item which will be added to the list. When I step back (pop) the list is not updated since the datasource is assigned in getInitialState.
How can I force the View to updated the list when I pop? Is this done by handling the route in onDidFocus of the navigator or is there anything I can add to the actual ListView?
Have provided a minified version of the code and it is the Budget-component and its list which needs to be updated when there has been a pop-event from the navigator.
Index-file
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)}
/>
}/>
)
}
});
Budget-component
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}
/>
);
}
};
Hope this is enough for you to understand the problem, thanks!
Edit: Tried to reach a function which will set a new state with data for the list from onDidFocus. But since the call to that function is via route.component.prototype.updateData(data), this.setState returns "Cannot read property 'enqueueSetState' of undefined"
Upvotes: 3
Views: 2425
Reputation: 336
I solved this problem using componentDidMount
, which is called every time that the Component appear. This solution is currently working for me.
For example,
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;
Upvotes: -1
Reputation: 1333
So I found out that this is supposedly a bug with the rendering of ListView in React Native and by adding some less beautiful code, I got it to work.
I added a method onDidFocus to my index-file which is called everytime a component is shown (this so it can be used when the user pops to the previous view). When the route in onDidFocus was Budget, I return the component again. I also moved out the data which is used in the ListView to my index so ListView gets the data as a property and will re-render when it has changed:
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)}
/>
}/>
)
}
});
Next in BudgetComponent.js I just set the data from props as a state and used that state-data to send it as props to my BudgetList-component (I also changed it to a class and not a component but that's not important):
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>
)
});
Now this should work but due to the bug, the DataSource in a List does not work correctly so by using a method componentWillUpdate (which is called all the time the List is viewed) I could force the update of data. However, in order to not get stuck in an endless loop, I had to check if the data should be updated or else it would get stuck in updating the data:
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>
)
}
});
Upvotes: 0