2016-07-08 8 views
6

W poniższym przykładzie:Próbowano ustawić klucz na obiekcie, który ma na celu być niezmienne i zostały zamrożone

  • MapView wyświetla elementy ListView jak adnotacji
  • Kliknięcie na element ListView powinien spowodować, że zostanie zamalowany na kolor niebieski.
  • Bonus jeśli MapView i ListView efektywnie wykorzystać obiekt państwowy

Modyfikacja DataSource z ListView wydaje się powodować konflikt, gdy atrybut active dostaje modyfikowany:

Próbowano ustawić klucz ' active 'z wartością' false 'na obiekcie , która ma być niezmienna i została zamrożona.

enter image description here

Jaki jest właściwy sposób ustalania stanu?

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); 

Odpowiedz

5

Problem

Po ustawieniu field.active = true; lub this.previousField.active = false;, jesteś modyfikacji obiektu (field), który jest obecny w kodzie źródłowym swojej ListView. ListView zgłasza błąd, ponieważ zamarza źródło danych podczas tworzenia go przy użyciu cloneWithRows. Ma to na celu zapewnienie, że źródła danych nie można zmodyfikować poza normalnym cyklem życia komponentu React (np. setState). Zamiast tego obiekty ListView.DataSource są przeznaczone do zmiany za pomocą cloneWithRows, która zwraca kopię istniejącego źródła danych.

Jeśli jesteś zaznajomiony z biblioteką Redux, jest bardzo podobna do filozofii posiadające funkcje redukcyjne zwrócić kopię państwa, zamiast modyfikowania istniejącego stanu.

Klonowanie DataSource

Aby rozwiązać ten problem, zamiast mutacji field obiektów w swojej funkcji handleClick, co naprawdę chcesz zrobić, to stworzyć nową tablicę danych z wartościami już ustawione (jak active) oraz następnie zadzwoń pod numer setState z nowym źródłem danych dla Twojego urządzenia ListView utworzonego za pomocą cloneWithRows. Jeśli to zrobisz, w ogóle nie potrzebujesz klucza annotations w swoim stanie.

Code jest prawdopodobnie bardziej pomocne niż słowa tutaj:

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), 
    }); 
} 

Mam nadzieję, że to pomoże!Oto fragment kodu zawierający cały opublikowany przeze mnie kod wraz z moimi modyfikacjami. Działa to dla mnie tak, jak opisałeś to na iOS przy użyciu React Native 0.29. Oznaczyłeś pytanie android-mapview, więc zakładam, że korzystasz z Androida, ale platforma nie powinna w tym przypadku robić różnicy.

'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);

+1

Współpracuje z platformy iOS i twoje wyjaśnienia tak naprawdę pomóc. –

+0

Świetnie! Jeśli to rozwiąże Twój problem, czy przyjmiesz odpowiedź? Dziękuję Ci! –

Powiązane problemy