Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React-Native Updating List View DataSource

I have an iOS app I am making with react-native. The Game class contains a ListView component. I set the state in the constructor and include a dataSource. I have a hardcoded array of data for right now that I store in a different state property (this.state.ds). Then in the componentDidMount I use the cloneWithRows method to clone my this.state.ds as my dataSource for the view. That is pretty standard as far as ListViews go and is working well. Here is the code:

/**  * Sample React Native App  * https://github.com/facebook/react-native  */ 'use strict';   var React = require("react-native");  var { StyleSheet, Text, View, ListView, TouchableHighlight } = React;  class Games extends React.Component { constructor(props) {     super(props);     var ds = new ListView.DataSource({         rowHasChanged: (r1, r2) => r1 != r2     });     this.state = {         ds: [             { AwayTeam: "TeamA", HomeTeam: "TeamB", Selection: "AwayTeam" },             { AwayTeam: "TeamC", HomeTeam: "TeamD", Selection: "HomeTeam" }         ],         dataSource: ds     }; }  componentDidMount() {     this.setState({         dataSource: this.state.dataSource.cloneWithRows(this.state.ds)     }); } pressRow(rowData) {     var newDs = [];     newDs = this.state.ds;     newDs[0].Selection = newDs[0] == "AwayTeam" ? "HomeTeam" : "AwayTeam";     this.setState({         dataSource: this.state.dataSource.cloneWithRows(newDs)     }); }  renderRow(rowData) {     return (         <TouchableHighlight             onPress={() => this.pressRow(rowData)}             underlayColor="#ddd"         >             <View style={styles.row}>                 <Text style={{ fontSize: 18 }}>                     {rowData.AwayTeam} @ {rowData.HomeTeam}{" "}                 </Text>                 <View style={{ flex: 1 }}>                     <Text style={styles.selectionText}>                         {rowData[rowData.Selection]}                     </Text>                 </View>             </View>         </TouchableHighlight>     ); } render() {     return (         <ListView             dataSource={this.state.dataSource}             renderRow={this.renderRow.bind(this)}         />     ); } } var styles = StyleSheet.create({  row: {     flex: 1,     flexDirection: "row",     padding: 18,     borderBottomWidth: 1,     borderColor: "#d7d7d7" }, selectionText: {     fontSize: 15,     paddingTop: 3,     color: "#b5b5b5",     textAlign: "right" } });   module.exports = Games 

The issue I am having comes in the pressRow method. When the user presses the row, I would like the selection to change and for it to render the change on the device. Through some debugging, I have noticed that even though I am changing the Selection property of the object in the newDs array, the same property changes on the object in this.state.ds and similarly changes the object in this.state.dataSource._dataBlob.s1. Through further debugging, I have found that since those other arrays have changed, the ListView's DataSource object doesn't recognize the change because when I set the state and rowHasChanged is called, the array it is cloning matches the array this.state.dataSource._dataBlob.s1 and so it doesn't look like a change and doesn't rerender.

Any ideas?

like image 235
Brandon M Avatar asked Jul 31 '15 05:07

Brandon M


People also ask

How to get started with React Native?

Getting started with React Native will help you to know more about the way you can make a React Native project. We are going to use react-native init to make our React Native App. Assuming that you have node installed, you can use npm to install the react-native-cli command line utility. Open the terminal and go to the workspace and run

How to make an expandable list view in React Native?

Example of Expandable List View in React Native. This is an Example of Expandable List View in React Native. To make an Expandable List View we will create an Expandable Component with the help of stylesheet. Expandable list view is like a sectioned list view but with hidden section content.

How to redraw a listview from an array of items?

Basically, there seems to be a bug with the ListView component and you need to rebuild each item that changes in the datasource for the ListView to redraw it. First, create the datasource and a copy of the array and save them to state. In my case, foods is the array.

How to change the array in the DataSource?

When you want to change something in the datasource, make a copy of the array you saved to the state, rebuild the item with the change and then save the new array with the change back to the state (both db and datasource). Show activity on this post. react is smart enough to detect changes in dataSource and if the list should be re-rendered.


2 Answers

Try this:

pressRow(rowData){      var newDs = [];     newDs = this.state.ds.slice();     newDs[0].Selection = newDs[0] == "AwayTeam" ? "HomeTeam" : "AwayTeam";     this.setState({       dataSource: this.state.dataSource.cloneWithRows(newDs)     })  } 

This should make a copy of the array, which can then be modified independently of the original array in this.state.ds.

like image 136
eddyjs Avatar answered Sep 17 '22 19:09

eddyjs


In case anyone comes across this issue like I did, here's the complete solution.

Basically, there seems to be a bug with the ListView component and you need to rebuild each item that changes in the datasource for the ListView to redraw it.

First, create the datasource and a copy of the array and save them to state. In my case, foods is the array.

constructor(props){     super(props);     var ds = new ListView.DataSource({         rowHasChanged: (row1, row2) => row1 !== row2,     });     this.state = {         dataSource: ds.cloneWithRows(foods),         db: foods,     }; } 

When you want to change something in the datasource, make a copy of the array you saved to the state, rebuild the item with the change and then save the new array with the change back to the state (both db and datasource).

onCollapse(rowID: number) {     console.log("rowID", rowID);     var newArray = this.state.db.slice();     newArray[rowID] = {         key: newArray[rowID].key,         details: newArray[rowID].details,         isCollapsed: newArray[rowID].isCollapsed == false ? true : false,     };     this.setState({         dataSource: this.state.dataSource.cloneWithRows(newArray),         db: newArray,     }); } 
like image 33
Dev01 Avatar answered Sep 19 '22 19:09

Dev01