You tried to set a key on an object that must be immutable and was frozen

In the following example:

  • MapView displays ListView items as annotations
  • Clicking on a ListView element should make it blue .
  • Bonus if MapView and ListView make good use of the state object

Changing the DataSource of ListView seems to cause a conflict when the active attribute changes:

You tried to set the key "active" with the value "false" to an object that must be immutable and was frozen.

enter image description here

What is the correct way to set state?

RNPlay example

 'use strict'; import React, {Component} from 'react'; import {AppRegistry,View,ListView,MapView,Text,TouchableOpacity} from 'react-native'; var annotations = [ { title: 'A',active: false,latitude: 45,longitude: 26,latitudeDelta: 0.015,longitudeDelta: 0.015, },{ title: 'B',active: false,latitude: 49,longitude: 14,latitudeDelta: 0.015,longitudeDelta: 0.015, },{ title: 'C',active: false,latitude: 26,longitude: 25,latitudeDelta: 0.015,longitudeDelta: 0.015, } ] class SampleApp extends Component { constructor(props) { super(props); var ds = new ListView.DataSource({ rowHasChanged: (row1, row2) => row1 !== row2, }); this.state = { region: annotations[0], annotations: annotations, dataSource: ds.cloneWithRows(annotations) }; } handleClick(field) { if (this.previousField) { this.previousField.active = false; } this.previousField = field; field.active = true; this.setState({ region: field, }); } renderField(field) { let color = (field.active == true)?'blue':'yellow'; return ( <TouchableOpacity onPress={this.handleClick.bind(this,field)}> <Text style={{backgroundColor:color,borderWidth:1}}>{field.title}</Text> </TouchableOpacity> ); } render() { return ( <View style={{flex:1,flexDirection:'column',alignSelf:'stretch'}}> <MapView style={{flex:0.5,alignSelf:'stretch',borderWidth:1}} region={this.state.region} annotations={this.state.annotations} /> <ListView dataSource={this.state.dataSource} renderRow={(field) => this.renderField(field)} /> </View> ); } } AppRegistry.registerComponent('SampleApp', () => SampleApp); 
+6
source share
1 answer

Problem

When you set field.active = true; or this.previousField.active = false; , you modify the field object that is present in the data source of your ListView . ListView throws an error because it freezes its data source when created with cloneWithRows . This is necessary so that the data source cannot be changed outside the normal life cycle of the React component (for example, setState ). Instead, ListView.DataSource objects are intended to be modified using cloneWithRows , which returns a copy of an existing data source.

If you are familiar with the Redux library, it is very similar to the philosophy that reducer functions return a copy of state, rather than modify an existing state.

Cloning a data source

To solve this problem, instead of mutating the field objects in your handleClick function, what you really want to do is create a new data array with the values ​​already set (for example, active ), and then call setState with a new data source for your ListView created using cloneWithRows . If you do this, you will not need the annotations key in your state at all.

The code is probably more useful than the words here:

 handleClick(field) { //iterate over annotations, and update them. //I'm taking 'title' as a unique id property for each annotation, //for the sake of the example. const newAnnotations = annotations.map(a => { //make a copy of the annotation. Otherwise you'll be modifying //an object that in your listView datasource, //and therefore frozen. let copyA = {...a}; if (copyA.title === field.title) { copyA.active = true; } else { copyA.active = false; } return copyA; }); this.setState({ region: {...field, active: true}, dataSource: this.state.dataSource.cloneWithRows(newAnnotations), }); } 

Hope this helps! Here is a code snippet containing the complete code that you posted, with my changes. It works for me the same way you described it on iOS using React Native 0.29. You noted the android-mapview question, so I assume you are using Android, but the platform should not make any difference in this case.

 'use strict'; import React, {Component} from 'react'; import {AppRegistry,View,ListView,MapView,Text,TouchableOpacity} from 'react-native'; var annotations = [ { title: 'A',active: false,latitude: 45,longitude: 26,latitudeDelta: 0.015,longitudeDelta: 0.015, },{ title: 'B',active: false,latitude: 49,longitude: 14,latitudeDelta: 0.015,longitudeDelta: 0.015, },{ title: 'C',active: false,latitude: 26,longitude: 25,latitudeDelta: 0.015,longitudeDelta: 0.015, } ] class SampleApp extends Component { constructor(props) { super(props); var ds = new ListView.DataSource({ rowHasChanged: (row1, row2) => row1 !== row2, }); this.state = { region: annotations[0], dataSource: ds.cloneWithRows(annotations) }; } handleClick(field) { //iterate over annotations, and update them. //I'm taking 'title' as a unique id property for each annotation, //for the sake of the example. const newAnnotations = annotations.map(a => { //make a copy of the annotation. Otherwise you'll be modifying //an object that in your listView datasource, //and therefore frozen. let copyA = {...a}; if (copyA.title === field.title) { copyA.active = true; } else { copyA.active = false; } return copyA; }); this.setState({ region: {...field, active: true}, dataSource: this.state.dataSource.cloneWithRows(newAnnotations), }); } renderField(field) { console.log(field); let color = (field.active == true)?'blue':'yellow'; return ( <TouchableOpacity onPress={this.handleClick.bind(this,field)}> <Text style={{backgroundColor:color,borderWidth:1}}>{field.title}</Text> </TouchableOpacity> ); } render() { return ( <View style={{flex:1,flexDirection:'column',alignSelf:'stretch'}}> <MapView style={{flex:0.5,alignSelf:'stretch',borderWidth:1}} region={this.state.region} annotations={this.state.annotations} /> <ListView dataSource={this.state.dataSource} renderRow={(field) => this.renderField(field)} /> </View> ); } } AppRegistry.registerComponent('SampleApp', () => SampleApp); 
+7
source

All Articles