React Native Picker:onValueChange 触发其他选择器的意外 onValueChange

React Native Picker: onValueChange triggers unintended onValueChange for other pickers

我正在尝试实现一个 select 或页面,用户可以在其中 select 基于 3 个位置属性(设施、楼层、套房)的室内地图。这 3 个属性的任何独特组合都保证具有独特的室内地图。我正在使用 React Native 的选择器下拉菜单让用户 select 他们想查看哪个设施、楼层和套房。根据 select 这些属性中的任何一个,它应该过滤其余属性的选项,以便存在有效组合。

过滤列表的算法对我来说不是问题。问题是,当我 select 一个选择器的选择器值时,它将其他选择器的值重置为 defaultPlacholder 值“---”,我不知道为什么。

每个选择器的 selectedValue 等同于存储当前 selected 设施、楼层或套房 ID 的状态变量。状态变量在开始时被初始化为'defaultPlaceholder'。从下拉菜单中选择不同的 ID 会调用 setState 来更改当前 selected ID 的 ID。至少,这是我所期望的。

相反,当从 onValueChange 调用 setState(state variable) 时,组件不会重新渲染一次,而是会重新渲染 MULTIPLE 次(大约 6-7 次)。对于一次渲染迭代,状态变量更改为所需的下拉项,然后自动重置为 defaultPlaceholder 值。不仅如此,它还会以某种方式触发其他选择器的 onValueChange,即使这些选择器的状态变量没有改变,只有选择器项目列表(例如 pickerFacilitiesList)发生了变化。现在,无论之前的状态值是什么,其他选择器项都将重置为 defaultPlaceholder 值。

更莫名其妙的是,这个问题不会出现在套房选择器上,只会出现在设施和楼层选择器上。

如有任何帮助,我们将不胜感激。

下方:render() 函数的一部分

    //.map iterates through uniqueFacilityIDs and generates a list of picker items
    const pickerFacilitiesList = uniqueFacilityIDs.map(a => {
      return <Picker.Item key={a.key} label={a.facilityName} value={a.facilityID} />;
    });    

    const pickerFloorsList = uniqueFloorIDs.map(a => {
      return <Picker.Item key={a.key} label={a.floorName} value={a.floorID} />;
    });

    const pickerSuitesList = uniqueSuiteIDs.map(a => {
      return <Picker.Item key={a.key} label={a.suiteName} value={a.suiteID} />;
    });


    if(this.state.pickerFacilityID === defaultPlaceholder || this.state.pickerFloorID === defaultPlaceholder || this.state.pickerSuiteID === defaultPlaceholder){
      return (
        <View>
          <Text style={{fontSize: 18, fontWeight: 'bold', textAlign: 'center', marginBottom: 25, marginTop: 15}}> Display Assets by Suites </Text> 

          <Text style={{fontSize: 16.5, textAlign: 'left', marginLeft: 30, marginBottom: 15, textDecorationLine: 'underline'}}>Filter suites by:</Text>

          <Text style={{fontSize: 16.5, textAlign: 'left', marginLeft: 30}}> Facility </Text>
          <Picker
            mode = 'dropdown'
            style = {{width: '80%', marginLeft: 30}}
            selectedValue={this.state.pickerFacilityID}
            onValueChange={(itemValue, itemIndex) => {
              this.setState({pickerFacilityID:itemValue})
            }}
          >
            <Picker.Item label = {defaultPlaceholder} value = {defaultPlaceholder}/>
            {pickerFacilitiesList}
          </Picker>  

          <Text style={{fontSize: 16.5, textAlign: 'left', marginLeft: 30}}> Floor </Text>
          <Picker
            mode = 'dropdown'
            style = {{width: '80%', marginLeft: 30}}
            selectedValue={this.state.pickerFloorID}
            onValueChange={(itemValue, itemIndex) => {
              this.setState({pickerFloorID:itemValue})
            }}
          >
            <Picker.Item label = {defaultPlaceholder} value = {defaultPlaceholder} />
            {pickerFloorsList}
          </Picker>

          <Text style={{fontSize: 16.5, textAlign: 'left', marginLeft: 30}}> Suite </Text>
          <Picker
            mode = 'dropdown'
            style = {{width: '80%', marginLeft: 30}}
            selectedValue={this.state.pickerSuiteID}
            onValueChange={(itemValue, itemIndex) => {
              this.setState({pickerSuiteID:itemValue})
            }}
          >
            <Picker.Item label = {defaultPlaceholder} value = {defaultPlaceholder} />
            {pickerSuitesList}
          </Picker>

          <TouchableHighlight 
            style={{ backgroundColor: 'gray', left: 200, width: 165, height: 40, justifyContent: 'center', alignItems: 'center', top: 10}}  
            activeOpacity = {1}
            onPress={() => {
              //Go to render map component 
            }}  
          >    
            <Text style = {{color: '#FFFFFF', fontSize: 18}}> Display suite map </Text>
          </TouchableHighlight>        
        </View>
      )

我建议您将选择器项生成器放在渲染方法之外,这样您的代码应该类似于以下内容:

(我试过这部分代码,它似乎工作得很好,但我没有实现你的逻辑)

import React from 'react';
import {
  View,
  Text,
  Picker,
  TouchableHighlight,
  ScrollView,
} from 'react-native';

const defaultPlaceholder = 'default placeholder';

const uniqueFacilityIDs = [
  {
    key: 'facility_key_1',
    facilityName: 'Facility Name 1',
    facilityID: 'facility_id_1'
  },
  {
    key: 'facility_key_2',
    facilityName: 'Facility Name 2',
    facilityID: 'facility_id_2'
  },
];

const uniqueFloorIDs = [
  {
    key: 'floor_key_1',
    floorName: 'Floor Name 1',
    floorID: 'floor_id_1'
  },
  {
    key: 'floor_key_2',
    floorName: 'Floor Name 2',
    floorID: 'floor_id_2'
  },
];

const uniqueSuiteIDs = [
  {
    key: 'suits_key_1',
    suiteName: 'Suits Name 1',
    suiteID: 'suits_id_1'
  },
  {
    key: 'suits_key_2',
    suiteName: 'Suits Name 2',
    suiteID: 'suits_id_2'
  },
];

class App extends React.Component {
  state = {
    pickerFacilityID: defaultPlaceholder,
    pickerFloorID: defaultPlaceholder,
    pickerSuiteID: defaultPlaceholder,
  };

  renderFacilitiesPickerItems = () => uniqueFacilityIDs.map(a => {
    return <Picker.Item key={a.key} label={a.facilityName} value={a.facilityID}/>;
  });

  renderFloorsPickerItems = () => uniqueFloorIDs.map(a => {
    return <Picker.Item key={a.key} label={a.floorName} value={a.floorID}/>;
  });

  renderSuitesPickerItems = () => uniqueSuiteIDs.map(a => {
    return <Picker.Item key={a.key} label={a.suiteName} value={a.suiteID}/>;
  });

  render() {
    if (this.state.pickerFacilityID === defaultPlaceholder || this.state.pickerFloorID === defaultPlaceholder || this.state.pickerSuiteID === defaultPlaceholder) {
      return (
          <ScrollView>
            <View>
              <Text style={{
                fontSize: 18,
                fontWeight: 'bold',
                textAlign: 'center',
                marginBottom: 25,
                marginTop: 15
              }}> Display Assets by Suites </Text>

              <Text style={{
                fontSize: 16.5,
                textAlign: 'left',
                marginLeft: 30,
                marginBottom: 15,
                textDecorationLine: 'underline'
              }}>Filter suites by:</Text>

              <Text style={{fontSize: 16.5, textAlign: 'left', marginLeft: 30}}> Facility </Text>
              <Picker
                  mode='dropdown'
                  style={{width: '80%', marginLeft: 30}}
                  selectedValue={this.state.pickerFacilityID}
                  onValueChange={(itemValue, _itemIndex) => {
                    this.setState({pickerFacilityID: itemValue})
                  }}
              >
                <Picker.Item label={defaultPlaceholder} value={defaultPlaceholder}/>
                {this.renderFacilitiesPickerItems()}
              </Picker>

              <Text style={{fontSize: 16.5, textAlign: 'left', marginLeft: 30}}> Floor </Text>
              <Picker
                  mode='dropdown'
                  style={{width: '80%', marginLeft: 30}}
                  selectedValue={this.state.pickerFloorID}
                  onValueChange={(itemValue, itemIndex) => {
                    this.setState({pickerFloorID: itemValue})
                  }}
              >
                <Picker.Item label={defaultPlaceholder} value={defaultPlaceholder}/>
                {this.renderFloorsPickerItems()}
              </Picker>

              <Text style={{fontSize: 16.5, textAlign: 'left', marginLeft: 30}}> Suite </Text>
              <Picker
                  mode='dropdown'
                  style={{width: '80%', marginLeft: 30}}
                  selectedValue={this.state.pickerSuiteID}
                  onValueChange={(itemValue, itemIndex) => {
                    this.setState({pickerSuiteID: itemValue})
                  }}
              >
                <Picker.Item label={defaultPlaceholder} value={defaultPlaceholder}/>
                {this.renderSuitesPickerItems()}
              </Picker>
            </View>
          </ScrollView>
      )
    } else {
      return (
          <TouchableHighlight
              style={{
                backgroundColor: 'gray',
                left: 200,
                width: 165,
                height: 40,
                justifyContent: 'center',
                alignItems: 'center',
                top: 10
              }}
              activeOpacity={1}
              onPress={() => {
                //Go to render map component
              }}
          >
            <Text style={{color: '#FFFFFF', fontSize: 18}}> Display suite map </Text>
          </TouchableHighlight>
      )
    }
  }
}

export default App;