简体   繁体   English

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

[英]React native navigation - Child not updating on parent update

I've edited a little bit the example from the react navigation official guide , in order to simulate a state change after 1 second in the HomeScreen. 为了从HomeScreen中模拟1秒后的状态变化,我已经对React Navigation官方指南中的示例进行了一些编辑。

I can't understand why the DetailScreen is not rerenderer when the parent screen state changes. 我不明白为什么父屏幕状态更改时DetailScreen无法重新渲染。 Any way to obtain such behaviour? 有什么办法获得这种行为?

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 />;
  }
}

Snack here 零食在这里

Why your code isn't working: 为什么您的代码无法正常工作:

You're passing value as a navigation prop from Home to Details . 您正在将value作为从HomeDetails的导航道具传递。 Unlike regular props, changing the value of a navigation prop in Home doesn't cause the value of the navigation prop itself to change. 与常规道具不同,在Home中更改导航道具的值不会导致导航道具本身的值发生变化。 So if Home were a parent component that had Details as a child component, like this: 因此,如果Home是具有Details作为子组件的父组件,如下所示:

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

then when this.state.value changes in Home , this.props.value automatically changes in Details . 然后,当this.state.valueHome更改时, this.props.value将在Details自动更改。 However, since Home and Details have a sibling relationship in the stack navigator, you aren't able to pass value as a regular prop; 但是,由于HomeDetails在堆栈导航器中具有同级关系,因此您不能将value作为常规属性传递; the only way to pass a prop from Home to Details is, as you've done, as a navigation param. 将道具从“ Home传递到“ Details的唯一方法就是将其作为导航参数。 The problem is that when you pass value as you've done: 问题在于,当您完成传递value时:

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

updating this.state.value in Home does not cause this.props.navigation.getParam('value') to automatically update. Home中更新this.state.value 不会导致this.props.navigation.getParam('value')自动更新。 So in your code: 因此,在您的代码中:

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

value remains what it was when it was initially passed. value仍然是最初通过时的value

SOLUTION

There are several possible workarounds, like manually forcing a re-render after the setTimeout or re-structuring your component hierarchy to make Home the parent of Details . 有几种可能的解决方法,例如在setTimeout之后手动强制重新渲染,或重新构造组件层次结构以将Home作为Details的父级。 However, I think the best way to solve the problem while preserving the structure of your app is the following: 但是,我认为在保留应用程序结构的同时解决问题的最佳方法是:

Instead of holding this.state.value in Home , hold it in App . 与其在Home中保存this.state.valuethis.state.valueApp保存它。 This follows the general principle that it's easier for a child to update a parent's state variables (or vice versa) than it is for a component to update its sibling's state variables. 这遵循一个普遍的原则,即相对于组件更新其同级的状态变量而言,孩子更容易更新父级的状态变量(反之亦然)。

Update to App component 更新到App组件

Since App is an AppContainer , you'll need to pass this.state.value to Details via screenprops. 由于AppAppContainer ,因此您需要通过screenprops将this.state.value传递给Details When you create any kind of navigator, screenprops are the way to pass variables to all the components in the navigator. 创建任何类型的导航器时,screenprops都是将变量传递给导航器中所有组件的方法。 So your App component will now look like this: 因此,您的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
    }}/>;
  }

}

Update to Home component 更新到Home组件

The only thing you'll change in your Home component will be the onPress function. 您在Home组件中唯一要更改的就是onPress函数。 First, you won't pass value as a navigation prop anymore since you'll be accessing the value as a screenProps passed from App to all screens, rather than as a navigation prop passed from Home to Details . 首先,您将不再将value作为导航道具传递,因为您将作为从App传递到所有屏幕的screenProps而不是从Home传递到Details的导航道具来访问值。 Second, instead of updating this.state of Home you'll be calling this.props.screenProps.updateValue() to update the state in App . 其次,不是更新Home this.state而是调用this.props.screenProps.updateValue()来更新App的状态。 So the onPress in your Home component will now look like this: 因此,您的Home组件中的onPress现在将如下所示:

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

Update to Details component 更新到Details组件

The only change to Details is that rather than displaying this.props.navigation.getParam('value') , you'll be displaying this.props.screenProps.appState.value since we're now getting value from the screenProps from App instead of as a navigation prop from Home . Details的唯一更改是,不是显示this.props.navigation.getParam('value') ,而是显示this.props.screenProps.appState.value因为我们现在是从App的screenProps获取value作为Home的导航道具。 So your Details component will now look like this: 因此,您的Details组件现在将如下所示:

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

WHOLE NEW CODE 全新代码

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() {
    const { screenProps } = this.props;
    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text>Home Screen</Text>
        <Button
          title="Click"
          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);
          }}
        />
      </View>
    );
  }
}

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

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

const AppContainer = createAppContainer(RootStack);

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={{
      appState: this.state,
      updateValue: this.updateValue
    }}/>;
  }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM