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?
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
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.
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.
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.
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
.
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, }); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With