如何在反应原生中导航到下一个组件时完成当前组件

时间:2021-07-23 18:58:50

Hi I am trying to navigate to next component using navigate function. I am using react-navigation for the navigation among multiple components.

嗨,我正在尝试使用导航功能导航到下一个组件。我在多个组件之间使用react-navigation进行导航。

Suppose I have index.android.js and DashboardScreen.js component. I am trying to navigate to DashboardScreen.js component from index component.

假设我有index.android.js和DashboardScreen.js组件。我试图从索引组件导航到DashboardScreen.js组件。

It is navigating but index component always retain in component stack. when I press back then it opens index.android.js which should not be. Does anyone know how to manage this in react-native. In Android, finish() works for this.

它正在导航,但索引组件始终保留在组件堆栈中。当我按回然后它打开index.android.js,这不应该。有谁知道如何在react-native中管理它。在Android中,finish()适用于此。

navigate("DashboardScreen");

When I am navigating from SplashScreen to EnableNotification then SplashScreen should be destroyed, if I am navigating from EnableNotification to CreateMessage then EnableNotification should be destroyed and if I am navigating from CreateMessage to DashboardScreen then CreateMessage should be destroyed. As of now no component is being destroyed.

当我从SplashScreen导航到EnableNotification时,SplashScreen应该被销毁,如果我从EnableNotification导航到CreateMessage,那么应该销毁EnableNotification,如果我从CreateMessage导航到DashboardScreen,那么应该销毁CreateMessage。截至目前,没有任何组件被销毁。

index.android.js

index.android.js

class SplashScreen extends Component {
  render() {
    if (__DEV__) {
      console.disableYellowBox = true;
    }

    const { navigate } = this.props.navigation;

    AsyncStorage.getItem("@ProductTour:key").then(value => {
      console.log(value);
      if (value) {
        navigate("DashboardScreen");
      }
    });

    return (
     ....
    );
  }
}

const App = StackNavigator(
  {
    Splash: {
      screen: SplashScreen,
      navigationOptions: {
        header: {
          visible: false
        }
      }
    },
    EnableNotification: {
      screen: EnableNotificationScreen,
      navigationOptions: {
        header: {
          visible: false
        }
      }
    },
    CreateMessage: {
      screen: CreateMessageScreen,
      navigationOptions: {
        header: {
          visible: false
        }
      }
    },
    DashboardScreen: {
      screen: DashboardScreen,
      navigationOptions: {
        header: {
          visible: false
        }
      }
    }
  },
  {
    initialRouteName: "Splash"
  }
);

3 个解决方案

#1


4  

First of all, using AsyncStorage in an a synchronous function (most especially a lifecycle one) is such a bad idea. You should typically keep ASyncStorage to places in your folder / app structure that make sense for where you access/keep data but since that's not the question I will just mention it quickly here...

首先,在同步函数(尤其是生命周期函数)中使用AsyncStorage是一个糟糕的主意。您通常应该将ASyncStorage保留在文件夹/应用程序结构中的位置,这些位置对于访问/保留数据的位置有意义,但由于这不是问题,我将在这里快速提及...

Basically you are asking to navigate once the ASync method completes itself based on EVERY render... Those new to RN should know that an awful lot of things can cause a render to fire. Some cases, the render function can fire (I have seen this many times before) 10 or more times before finalizing the last render. This means you would have fired that ASyncStorage method 10 times... definitely something to think about when implementing this stuff. So more or less, the .then(); part of the AsyncStorage function is firing long after the render has already finished doing it's thing. If it was a reasonable approach to use I would say to put the return part of the render function inside of the .then((value) => { return ( ... ); });. But this is an even worse idea. Basically you need the right lifecycle method here and it's NOT the render method.

基本上,当ASync方法基于每个渲染完成自身时,您要求导航... RN的新手应该知道很多东西都可能导致渲染。在某些情况下,渲染函数可以在完成最后一次渲染之前触发(我之前已多次看到)10次或更多次。这意味着您可能已经将该ASyncStorage方法解雇了10次......在实现这些内容时一定要考虑一下。或多或少,.then(); AsyncStorage函数的一部分在渲染完成之后很长时间才会触发。如果这是一种合理的使用方法,我会说将渲染函数的返回部分放在.then((value)=> {return(...);});中。但这是一个更糟糕的想法。基本上你需要正确的生命周期方法,它不是渲染方法。

Anyway, since I have never used this component library before I can only help nudge you in the right direction so here goes... These docs on their webpage seem to say that you need a reference to the props navigator passed down to the component in which you are using it. So if you created the navigator in this class, you would use this.refs.whateverYouNamedTheNavigatorReference.navigate('SomeItemName'). If you are in the class that has been passed this navigator as a prop, you use this.props.passNavigatorPropName.navigate('SomeItemName'). I see you are using variable deconstruction to get the navigate callback but I would caution on doing this, this way because I have seen it cause errors by grabbing an old version of the navigate function or its parent reference by accident and causing a cascading error effect.

无论如何,因为我之前从未使用过这个组件库,所以我只能帮助你朝着正确的方向推动你,所以这里...这些文档在他们的网页上似乎说你需要引用传递给组件的props导航器你正在使用它。因此,如果您在此类中创建了导航器,则可以使用this.refs.whateverYouNamedTheNavigatorReference.navigate('SomeItemName')。如果您在已将此导航器作为prop传递的类中,则使用this.props.passNavigatorPropName.navigate('SomeItemName')。我看到你正在使用变量解构来获得导航回调,但我会谨慎这样做,因为我看到它通过意外地抓取旧版本的导航功能或其父引用并导致级联错误效果而导致错误。

Also, if you are going to be using ASyncStorage in a component file (again, would recommend putting this in a component/class where your data is accessed throughout the app...) and you are going to use it to decide the app should navigate forwards/backwards... definitely remove it from the render function and put it in maybe the constructor, componentWillReceiveProps, componentDidReceiveProps or componentWillUpdate lifecycle functions. That way it fires based on an update, a new passed prop obj or one time as the component is built. Anything is better than firing it every single render.

此外,如果您要在组件文件中使用ASyncStorage(同样,建议将其放在整个应用程序中访问数据的组件/类中),并且您将使用它来决定应用程序应该向前/向后导航...绝对将其从渲染函数中移除并将其放入构造函数,componentWillReceiveProps,componentDidReceiveProps或componentWillUpdate生命周期函数中。这样,它会根据更新,新传递的prop obj或构建组件的一次触发。任何东西都比每次渲染都要好。

Lastly, I do not know what you have setup for your StackNavigator route stack object but you would need to have the keyword you used "DashboardScreen" in there pointing to an actual component that has been imported properly. The "DashboardScreen" keyword most likely would connect in your StackNavigator object to some component import like so...

最后,我不知道您为StackNavigator路由堆栈对象设置了什么,但是您需要在其中使用“DashboardScreen”关键字指向已正确导入的实际组件。 “DashboardScreen”关键字很可能会在您的StackNavigator对象中连接到某些组件导入,如此...

import Dashboard from '../Views/DashboardScreenView';

从'../Views/DashboardScreenView'导入仪表板;

StackNavigator({
  DashboardScreen: {
    screen: Dashboard,
    path: 'dashboard/:main',
    navigationOptions: null,
  },
});

#2


3  

Based on your requirement, i suggest following setup:

根据您的要求,我建议以下设置:

SplashNavigator.js

SplashNavigator.js

const SplashNavigator = StackNavigator({
  Splash: {
    screen: SplashScreen,
    navigationOptions: {
      header: {
        visible: false
      }
    }
  }
});

AppNavigator.js

AppNavigator.js

const AppNavigator = StackNavigator(
  {
    EnableNotification: {
      screen: EnableNotificationScreen,
      navigationOptions: {
        header: {
          visible: false
        }
      }
    },
    CreateMessage: {
      screen: CreateMessageScreen,
      navigationOptions: {
        header: {
          visible: false
        }
      }
    },
    Dashboard: {
      screen: DashboardScreen,
      navigationOptions: {
        header: {
          visible: false
        }
      }
    }
  },
  {
    initialRouteName: "EnableNotification"
  }
);

In your index.android.js, you will render the SplashNavigator.

在index.android.js中,您将渲染SplashNavigator。

The SplashNavigator will render the SplashScreen. It has initial state value isReady set to false, so it will render a loading text until the @ProductTour:key value from AsyncStorage is loaded (AsyncStorage is async function, u should not put it in your render function). It will then render your AppNavigator and render your EnableNotification as initial route.

SplashNavigator将渲染SplashScreen。它将初始状态值isReady设置为false,因此它将呈现加载文本,直到加载AsyncStorage的@ProductTour:键值为止(AsyncStorage是异步函数,不应该将它放在渲染函数中)。然后它将渲染您的AppNavigator并将EnableNotification渲染为初始路径。

class SplashScreen extends Component {
  constructor() {
    super(props);
    this.state = {
      isReady: false,
    }
  }

  componentDidMount() {
    AsyncStorage.getItem("@ProductTour:key").then(value => {
      console.log(value);
      // you will need to handle case when `@ProductTour:key` is not exists
      this.setState({
        isReady: true,
      });
    });
  }

  render() {
    const { isReady } = this.state;
    return (
      <View style={{flex: 1}}>
        {
          isReady ?
          <AppNavigator />
          : <Text>Loading</Text>
        }
      </View>
    );
  }
}

Then on EnableNotificationScreen and CreateMessageScreen, change your navigate route function to use NavigationActions.reset from doc

然后在EnableNotificationScreen和CreateMessageScreen上,更改导航路由功能以使用doc中的NavigationActions.reset

Example:

例:

import { NavigationActions } from 'react-navigation';

handleOnPressButton = () => {
  const resetAction = NavigationActions.reset({
    index: 0,
    actions: [
      NavigationActions.navigate({ routeName: "CreateMessage" })
    ]
  });
  this.props.navigation.dispatch(resetAction);
}

#3


0  

Yes in react native you can finish the current screen before navigating to new screen with the help of NavigationActions . Please refer this link -

是的本地反应您可以在NavigationActions的帮助下导航到新屏幕之前完成当前屏幕。请参考此链接 -

http://androidseekho.com/others/reactnative/finish-current-screen-on-navigating-another-in-react-native/

http://androidseekho.com/others/reactnative/finish-current-screen-on-navigating-another-in-react-native/

#1


4  

First of all, using AsyncStorage in an a synchronous function (most especially a lifecycle one) is such a bad idea. You should typically keep ASyncStorage to places in your folder / app structure that make sense for where you access/keep data but since that's not the question I will just mention it quickly here...

首先,在同步函数(尤其是生命周期函数)中使用AsyncStorage是一个糟糕的主意。您通常应该将ASyncStorage保留在文件夹/应用程序结构中的位置,这些位置对于访问/保留数据的位置有意义,但由于这不是问题,我将在这里快速提及...

Basically you are asking to navigate once the ASync method completes itself based on EVERY render... Those new to RN should know that an awful lot of things can cause a render to fire. Some cases, the render function can fire (I have seen this many times before) 10 or more times before finalizing the last render. This means you would have fired that ASyncStorage method 10 times... definitely something to think about when implementing this stuff. So more or less, the .then(); part of the AsyncStorage function is firing long after the render has already finished doing it's thing. If it was a reasonable approach to use I would say to put the return part of the render function inside of the .then((value) => { return ( ... ); });. But this is an even worse idea. Basically you need the right lifecycle method here and it's NOT the render method.

基本上,当ASync方法基于每个渲染完成自身时,您要求导航... RN的新手应该知道很多东西都可能导致渲染。在某些情况下,渲染函数可以在完成最后一次渲染之前触发(我之前已多次看到)10次或更多次。这意味着您可能已经将该ASyncStorage方法解雇了10次......在实现这些内容时一定要考虑一下。或多或少,.then(); AsyncStorage函数的一部分在渲染完成之后很长时间才会触发。如果这是一种合理的使用方法,我会说将渲染函数的返回部分放在.then((value)=> {return(...);});中。但这是一个更糟糕的想法。基本上你需要正确的生命周期方法,它不是渲染方法。

Anyway, since I have never used this component library before I can only help nudge you in the right direction so here goes... These docs on their webpage seem to say that you need a reference to the props navigator passed down to the component in which you are using it. So if you created the navigator in this class, you would use this.refs.whateverYouNamedTheNavigatorReference.navigate('SomeItemName'). If you are in the class that has been passed this navigator as a prop, you use this.props.passNavigatorPropName.navigate('SomeItemName'). I see you are using variable deconstruction to get the navigate callback but I would caution on doing this, this way because I have seen it cause errors by grabbing an old version of the navigate function or its parent reference by accident and causing a cascading error effect.

无论如何,因为我之前从未使用过这个组件库,所以我只能帮助你朝着正确的方向推动你,所以这里...这些文档在他们的网页上似乎说你需要引用传递给组件的props导航器你正在使用它。因此,如果您在此类中创建了导航器,则可以使用this.refs.whateverYouNamedTheNavigatorReference.navigate('SomeItemName')。如果您在已将此导航器作为prop传递的类中,则使用this.props.passNavigatorPropName.navigate('SomeItemName')。我看到你正在使用变量解构来获得导航回调,但我会谨慎这样做,因为我看到它通过意外地抓取旧版本的导航功能或其父引用并导致级联错误效果而导致错误。

Also, if you are going to be using ASyncStorage in a component file (again, would recommend putting this in a component/class where your data is accessed throughout the app...) and you are going to use it to decide the app should navigate forwards/backwards... definitely remove it from the render function and put it in maybe the constructor, componentWillReceiveProps, componentDidReceiveProps or componentWillUpdate lifecycle functions. That way it fires based on an update, a new passed prop obj or one time as the component is built. Anything is better than firing it every single render.

此外,如果您要在组件文件中使用ASyncStorage(同样,建议将其放在整个应用程序中访问数据的组件/类中),并且您将使用它来决定应用程序应该向前/向后导航...绝对将其从渲染函数中移除并将其放入构造函数,componentWillReceiveProps,componentDidReceiveProps或componentWillUpdate生命周期函数中。这样,它会根据更新,新传递的prop obj或构建组件的一次触发。任何东西都比每次渲染都要好。

Lastly, I do not know what you have setup for your StackNavigator route stack object but you would need to have the keyword you used "DashboardScreen" in there pointing to an actual component that has been imported properly. The "DashboardScreen" keyword most likely would connect in your StackNavigator object to some component import like so...

最后,我不知道您为StackNavigator路由堆栈对象设置了什么,但是您需要在其中使用“DashboardScreen”关键字指向已正确导入的实际组件。 “DashboardScreen”关键字很可能会在您的StackNavigator对象中连接到某些组件导入,如此...

import Dashboard from '../Views/DashboardScreenView';

从'../Views/DashboardScreenView'导入仪表板;

StackNavigator({
  DashboardScreen: {
    screen: Dashboard,
    path: 'dashboard/:main',
    navigationOptions: null,
  },
});

#2


3  

Based on your requirement, i suggest following setup:

根据您的要求,我建议以下设置:

SplashNavigator.js

SplashNavigator.js

const SplashNavigator = StackNavigator({
  Splash: {
    screen: SplashScreen,
    navigationOptions: {
      header: {
        visible: false
      }
    }
  }
});

AppNavigator.js

AppNavigator.js

const AppNavigator = StackNavigator(
  {
    EnableNotification: {
      screen: EnableNotificationScreen,
      navigationOptions: {
        header: {
          visible: false
        }
      }
    },
    CreateMessage: {
      screen: CreateMessageScreen,
      navigationOptions: {
        header: {
          visible: false
        }
      }
    },
    Dashboard: {
      screen: DashboardScreen,
      navigationOptions: {
        header: {
          visible: false
        }
      }
    }
  },
  {
    initialRouteName: "EnableNotification"
  }
);

In your index.android.js, you will render the SplashNavigator.

在index.android.js中,您将渲染SplashNavigator。

The SplashNavigator will render the SplashScreen. It has initial state value isReady set to false, so it will render a loading text until the @ProductTour:key value from AsyncStorage is loaded (AsyncStorage is async function, u should not put it in your render function). It will then render your AppNavigator and render your EnableNotification as initial route.

SplashNavigator将渲染SplashScreen。它将初始状态值isReady设置为false,因此它将呈现加载文本,直到加载AsyncStorage的@ProductTour:键值为止(AsyncStorage是异步函数,不应该将它放在渲染函数中)。然后它将渲染您的AppNavigator并将EnableNotification渲染为初始路径。

class SplashScreen extends Component {
  constructor() {
    super(props);
    this.state = {
      isReady: false,
    }
  }

  componentDidMount() {
    AsyncStorage.getItem("@ProductTour:key").then(value => {
      console.log(value);
      // you will need to handle case when `@ProductTour:key` is not exists
      this.setState({
        isReady: true,
      });
    });
  }

  render() {
    const { isReady } = this.state;
    return (
      <View style={{flex: 1}}>
        {
          isReady ?
          <AppNavigator />
          : <Text>Loading</Text>
        }
      </View>
    );
  }
}

Then on EnableNotificationScreen and CreateMessageScreen, change your navigate route function to use NavigationActions.reset from doc

然后在EnableNotificationScreen和CreateMessageScreen上,更改导航路由功能以使用doc中的NavigationActions.reset

Example:

例:

import { NavigationActions } from 'react-navigation';

handleOnPressButton = () => {
  const resetAction = NavigationActions.reset({
    index: 0,
    actions: [
      NavigationActions.navigate({ routeName: "CreateMessage" })
    ]
  });
  this.props.navigation.dispatch(resetAction);
}

#3


0  

Yes in react native you can finish the current screen before navigating to new screen with the help of NavigationActions . Please refer this link -

是的本地反应您可以在NavigationActions的帮助下导航到新屏幕之前完成当前屏幕。请参考此链接 -

http://androidseekho.com/others/reactnative/finish-current-screen-on-navigating-another-in-react-native/

http://androidseekho.com/others/reactnative/finish-current-screen-on-navigating-another-in-react-native/