反应本机导航-子级不更新父级更新

时间:2019-06-03 15:26:20

标签: javascript react-native react-native-navigation

我已经对react navigation official guide中的示例进行了一些编辑,以模拟HomeScreen 1秒后的状态变化。

当父屏幕状态更改时,我不明白为什么DetailScreen无法重新渲染。有什么办法获得这种行为?

import React from 'react';
import { Button, View, Text } from 'react-native';
import { createStackNavigator, createAppContainer } from 'react-navigation';

class HomeScreen extends React.Component {
  state = { value: 10 }
  render() {
    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text>Home Screen</Text>
        <Button
          title="Click"
          onPress={() => {
            const { value } = this.state;
            this.props.navigation.navigate('Detail', { value });
            setTimeout(() => {
              this.setState({ value: value + 1 })
            }, 1000);
          }}
        />
      </View>
    );
  }
}

class DetailScreen extends React.Component {
  render() {
    const { navigation } = this.props;
    const value = navigation.getParam('value');
    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text>Detail Screen</Text>
        <Text>{value}</Text>
      </View>
    );
  }
}

const RootStack = createStackNavigator(
  {
    Home: HomeScreen,
    Detail: DetailScreen,
  },
  {
    initialRouteName: 'Home',
  }
);

const AppContainer = createAppContainer(RootStack);

export default class App extends React.Component {
  render() {
    return <AppContainer />;
  }
}

小吃here

1 个答案:

答案 0 :(得分:2)

您的代码为何不起作用:

您正在将value作为导航道具从Home传递到Details。与常规道具不同,在Home中更改导航道具的值不会导致导航道具本身的值发生变化。因此,如果Home是具有Details作为子组件的父组件,则像这样:

class HomeScreen extends React.Component {
  ...
  <DetailsScreen
    value: this.state.value
  />
  ...
}

然后,当this.state.value中的Home变化时,this.props.value中的Details自动变化。但是,由于HomeDetails在堆栈导航器中具有同级关系,因此您无法将value作为常规道具传递;将道具从Home传递到Details的唯一方法是,作为导航参数。问题是当您通过value时,您已经完成以下操作:

const { value } = this.state;
this.props.navigation.navigate('Detail', { value });

更新this.state.value中的Home不会不会导致this.props.navigation.getParam('value')自动更新。因此,在您的代码中:

const value = navigation.getParam('value');
<Text>{value}</Text>

value保持最初通过时的状态。

解决方案

有几种可能的解决方法,例如在setTimeout之后手动强制重新渲染,或重新构建组件层次结构以使HomeDetails的父级。但是,我认为在保留应用程序结构的同时解决问题的最佳方法是:

与其将this.state.value保持在Home中,不如将其保持在App中。这遵循一个普遍的原则,即相对于组件更新其同级的状态变量而言,孩子更容易更新父级的状态变量(反之亦然)。

更新为App组件

由于AppAppContainer,因此您需要通过screenprops将this.state.value传递给Details。创建任何类型的导航器时,screenprops都是将变量传递给导航器中所有组件的方法。因此,您的App组件现在将如下所示:

export default class App extends React.Component {

  state = {value: 10} // state in App now instead of Home

  updateValue = (value) => { // method to update App's state, passed to children
    this.setState({value})
  }

  render() {
    return <AppContainer screenProps={{
      parentState: this.state,
      updateValue: this.updateValue
    }}/>;
  }

}

更新为Home组件

Home组件中唯一要更改的是onPress函数。首先,您将不再将value作为导航道具传递,因为您将以从App传递给所有屏幕的screenProps而不是从{{1 }}到Home。其次,您将调用Details来更新this.state中的状态,而不是更新Home中的this.props.screenProps.updateValue()。因此,App组件中的onPress看起来像这样:

Home

更新为onPress={() => { this.props.navigation.navigate('Detail'); // no navigation prop setTimeout(() => { screenProps.updateValue(screenProps.appState.value + 1) // updating state of App rather than state of Home }, 1000); }} 组件

Details的唯一更改是,您将显示Details而不是显示this.props.navigation.getParam('value'),因为我们现在从{{ 1}},而不是来自this.props.screenProps.appState.value的导航道具。因此,您的value组件现在将如下所示:

App

全新代码

Home