在以下示例中:

  • MapViewListView的元素显示为annotations
  • ListView个元素上的Clicking应该导致将其绘制为blue色.
  • 如果MapViewListView有效地使用状态对象,则奖励

active属性被修改时,修改ListView中的DataSource似乎会导致冲突:

您试图将密钥设置为"active",并将其值设置为"false"

enter image description here

设置状态的正确方式是什么?

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

推荐答案

The Problem

当设置field.active = true;this.previousField.active = false;时,您正在修改ListView的数据源中存在的对象(field).ListView抛出错误,因为当您使用cloneWithRows创建数据源时,它会冻结其数据源.这是为了确保数据源不能在正常的React组件生命周期(如setState)之外进行修改.取而代之的是,ListView.DataSource个对象被设计为使用cloneWithRows进行更改,cloneWithRows返回现有数据源的copy.

如果您熟悉Redux库,这与让reducer函数返回copy状态的原理非常相似,而不是修改现有状态.

Cloning the DataSource

为了解决这个问题,您真正想要做的不是在handleClick函数中对field个对象进行变异,而是创建一个具有已设置值(如active)的新数据数组,然后使用cloneWithRows创建的ListView的新数据源调用setState.如果你这样做,你实际上根本不需要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),
  });
}

我希望这有帮助!下面是一段代码片段,包含您发布的完整代码,以及我的修改.正如您在iOS上使用React Native 0.29所描述的那样,它对我有效.你标记了问题android mapview,所以我假设你运行的是android,但在这种情况下,平台不应该真的有什么不同.

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

React-native相关问答推荐

如何在Reaction Native中将身体分成三部分

React Native 无法下载模板

'未能找到 React 根视图的片段' React Native Android RNScreens

如何在react-navigation 中将drawer抽屉放在标题上?

如何获取 iOS 版本?

React Native 白色黑屏问题

react-native-firebase crashlytics 未显示在 firebase 仪表板上

在 React Native 中使用多个上下文提供程序的更好方法

你能在 Ubuntu 上构建 React Native 应用程序(Android 应用程序)吗?

React-Native 构建失败的应用程序:mergeDebugAssets

Property body[41] of BlockStatement expected node to be of a type ["Statement"] but instead got "AssignmentExpression"

null 不是对象(判断 this.state.count)

如何将 Leaflet.js 与 react-native 一起使用

React Native vs Swift/Objective-C/Java Native

React Native require(image) 返回数字

React Native + React (for web) 种子元素

React Native 动画 - zoom 时 translateX 和 translateY

在 navigator pop 上react Native Pass 属性

React Native font outline / textShadow

React Native 构建命令失败:PhaseScriptExecution ... (domain=NSPOSIXErrorDomain, code=2)