En el siguiente ejemplo:

  • MapView muestra los elementos de un ListView como anotaciones
  • Clic en un ListView elemento debe resultar en la pintura en azul color.
  • Bonificación si el MapView y ListView utilizar eficazmente el objeto de estado de

La modificación de la DataSource de ListView parece ser la causa de los conflictos cuando el active atributo se modificó:

Se ha intentado establecer la clave de ‘activo’ con el valor ‘false’ en un
objeto que está destinado a ser inmutable y ha sido congelado.

Se ha intentado establecer la clave en un objeto que está destinado a ser inmutable y ha sido congelado

¿Cuál es la forma correcta de establecer el estado?

RNPlay Ejemplo

'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);
InformationsquelleAutor Peter Gerhat | 2016-07-08

1 Comentario

  1. 19

    El Problema

    Cuando se establece field.active = true; o this.previousField.active = false;, que se está modificando un objeto (field) que está presente en el origen de su ListView. El ListView lanza un error porque se congela su origen de datos cuando se crea mediante cloneWithRows. Esto es para asegurar que el origen de datos no puede ser modificado fuera de la normal de Reaccionar componente del ciclo de vida (como setState). En su lugar, ListView.DataSource objetos están diseñados para ser cambiado con cloneWithRows, que devuelve un copia del origen de datos existente.

    Si estás familiarizado con el Redux de la biblioteca, es muy similar a la filosofía de tener reductor de las funciones devuelven un copia del estado, en lugar de modificar el estado existente.

    Clonación el origen de datos

    Para resolver este problema, en lugar de la mutación de field objetos en su handleClick función, lo que realmente quiero hacer es crear una nueva matriz de datos con valores ya establecidos (como active) y, a continuación, llamar a setState con un nuevo origen de datos para su ListView creado con cloneWithRows. Si usted hace esto, usted realmente no necesita ni siquiera el annotations clave en su estado en todo.

    Código es probablemente más útil que las palabras aquí:

    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's in your listView's 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),
    });
    }

    Espero que esto ayude! He aquí un fragmento de código que contiene el código que has publicado, con mis modificaciones. Se está trabajando para mí como lo has descrito debe en iOS usando Reaccionar Nativo de 0.29. Has etiquetado a la pregunta de android-mapview, así que estoy asumiendo que usted está ejecutando Android, pero la plataforma no debe realmente hacer una diferencia en este caso.

    JS:

    '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's in your listView's 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);

    • Funciona con la plataforma ios y sus explicaciones hizo realmente ayuda.
    • Genial!!! Si se resuelve el problema, tendría que aceptar la respuesta? Gracias!

Dejar respuesta

Please enter your comment!
Please enter your name here