反应本地更新状态“独立”

时间:2019-07-29 18:03:08

标签: react-native react-navigation mobile-development

我有两个屏幕,一个列表(Flatlist)和一个过滤器屏幕,我希望能够为列表设置一些过滤器。列表屏幕的状态为“数据”和“ usedFilters”。当我切换到过滤器屏幕时,将状态设置为用于响应导航的导航参数,然后通过navigation.navigate以及onChange函数作为过滤器屏幕的道具。在那里读取它们,并设置过滤器屏幕类的状态(通常使用列表屏幕中已通过过滤器的过滤器,如果未传递有效过滤器,则将初始化某些过滤器)。 之后,可以更改过滤器。如果发生这种情况,过滤器屏幕的状态将被更新。 如果然后单击应用按钮,则过滤器屏幕的状态将传递到onChange函数,并通过该状态返回到列表屏幕,onChange函数将更新列表屏幕的状态“ usedFilters”。如果按下取消按钮,则将null传递给onChange函数,并且不会调用setState。

为列表屏幕设置新状态非常正常。问题是,当我按下“取消”按钮(或由React导航自动显示的“后退”按钮)时,更改仍然保留。仅在状态已更改之前,这种情况才会发生。因此,如果从未应用过更改,因此列表屏幕的“ usedFitlers”状态为空,则不会发生此行为。仅当我已经进行了一些更改并且因此列表屏幕的“ usedFitlers”状态具有有效值并传递到过滤器屏幕时,取消或返回按钮才能按预期工作。

我正在使用expo-cli 3,并在我的android智能手机和iOS模拟器上进行了尝试。行为相同。我也使用chrome开发工具进行了调查,但我根本不知道“ usedFitlers”状态的更新位置。

我正在使用React Native 0.60和React Navigation 3.11.0

我最好的猜测是,由于某种原因,这两个状态共享相同的内存,或者一个是指向另一个或类似对象的指针。 (前一段时间在python中出现了类似的问题,不知道在分配变量时它使用了指针)。 有人知道吗?

列表屏幕:

export default class ListScreen extends React.Component {
    state = { data: [], usedFilters: null };


    static navigationOptions = ({ navigation }) => {
    let data = navigation.getParam('data')
    let changefilter = navigation.getParam('changeFilter')
    let currfilter = navigation.getParam('currFilter')
    return {
        headerTitle:
            <Text style={Styles.headerTitle}>{strings('List')}</Text>,
        headerRight: (
            <TouchableOpacity
            onPress={() => navigation.navigate('FilterScreen', {
            dataset: data, onChange: changefilter, activeFilters:
            currfilter })} >
            <View paddingRight={16}>
                <Icon name="settings" size={24} color=
                {Colors.headerTintColor} />
            </View>
        </TouchableOpacity>
        ),
    };
    };

    _onChangeFilter = (newFilter) => {

    if (newFilter) {
      this.setState({ usedFilters: newFilter })
      this.props.navigation.setParams({ currFilter: newFilter });
    } // added for debugging reasons
    else {
      this.forceUpdate();
      let a = this.state.usedFilters;
    }
  }


  _fetchData() {
    this.setState({ data: fakedata.results },
      () => this.props.navigation.setParams({ data:     fakedata.results,
      changeFilter: this._onChangeFilter }));

  }

  componentDidMount() {
      this._fetchData();
  }


  render() {
      return (
          <ScrollView>
              <FlatList/>
               // Just data rendering, no problems here        
          </ScrollView>
      );
  }
}

过滤器屏幕:

export default class FilterScreen extends React.Component {

  static navigationOptions = () => {
    return {
      headerTitle: <Text style={Styles.headerTitle}>    {strings('filter')}
      </Text>
    };
  };

  state = { currentFilters: null }

  _onChange = (filter, idx) => {
    let tmp = this.state.currentFilters;
    tmp[idx] = filter;
    this.setState({ currentFilters: tmp })
  }


  _initFilterElems() {
    const filters = this.props.navigation.getParam('activeFilters');
    const dataset = this.props.navigation.getParam('dataset');
    let filterA = [];
    let filterB = [];
    let filterC = [];
    if (filters) {
       // so some checks
    } else {
       // init filters
    }

    const filterElements = [filterA, filterB, filterC];
    this.setState({ currentFilters: filterElements })
  }

  componentDidMount() {
    this._initFilterElems()
  }

  render() {
    const onChange = this.props.navigation.getParam('onChange');
    return (
      <ScrollView style={Styles.screenView}>
        <FlatList
          data={this.state.currentFilters} // Listeneinträge
          keyExtractor={(item, index) => 'key' + index}
          renderItem={({ item, index }) => (  
            <FilterCategory filter={item} name={filterNames[index]}
        idx={index} onChange={this._onChange} />
          )}
          ItemSeparatorComponent={() => <View style=
          {Styles.listSeperator} />}
        />
        <View style={Layout.twoHorizontalButtons}>
          <TouchableOpacity onPress={() => {
            onChange(this.state.currentFilters);
            this.setState({ currentFilters: null });
            this.props.navigation.goBack();
          }}>
            <View style={Styles.smallButton}>
              <Text style={Styles.buttonText}>{strings('apply')}    </Text>
            </View>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => {
            onChange(null);
            this.setState({ currentFilters: null });
            this.props.navigation.goBack();
          }}>
            <View style={Styles.smallButton}>
              <Text style={Styles.buttonText}>{strings('cancel')}
              </Text>
            </View>
          </TouchableOpacity>
        </View>
      </ScrollView >
    );
  }
}

因此,当我按下“取消”按钮时,列表屏幕的_onChangeFilter函数将返回null。此部分有效,根据console.log和调试器,不会调用setState。但是,如果我在else部分中设置断点,则可以看到this.state.usedFilters已更改。

1 个答案:

答案 0 :(得分:0)

好一会儿我就知道了。问题在于整个过滤器列表总是被引用,因为即使更改列表的子部分,react native(js)似乎总是使用引用。

使用lodash cloneDeep解决了该问题。