Navigate to root screen from nested stack navigator

情到浓时终转凉″ 提交于 2019-12-02 00:25:08

Modal StackNavigator containing a Dismissable StackNavigator

Requires: react-navigation version: 1.0.0

Goal: Navigate from App TabNavigator to Screen 1 to Screen 2 to Screen N and then directly back to App TabNavigator.

Navigation hierarchy:

  • RootNavigator StackNavigator {mode: 'modal'}
    • App TabNavigator
      • TabA Screen
      • TabB Screen
      • TabC Screen
    • ModalScreen Screen
    • ModalStack DismissableStackNavigator
      • Screen 1 ModalStackScreen
      • Screen 2 ModalStackScreen
      • Screen N ModalStackScreen

Demo

package.json

{
  "name": "HelloWorld",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest"
  },
  "dependencies": {
    "react": "16.0.0-alpha.6",
    "react-native": "0.44.0",
    "react-navigation": "^1.0.0"
  },
  "devDependencies": {
    "babel-jest": "20.0.3",
    "babel-preset-react-native": "1.9.2",
    "jest": "20.0.3",
    "react-test-renderer": "16.0.0-alpha.6"
  },
  "jest": {
    "preset": "react-native"
  }
}

index.ios.js (or index.android.js)

import React from 'react'
import {
  AppRegistry,
  Button,
  Text,
  View
} from 'react-native'
import {
  StackNavigator,
  TabNavigator
} from 'react-navigation'

class TabA extends React.Component {
  state = {
    startScreen: 'none',
    returnScreen: 'none'
  }
  render () {
    return (
      <View style={{ padding: 40, paddingTop: 64 }}>
        <Text style={{ fontSize: 20 }}>{this.constructor.name}</Text>
        <Text>startScreen: {this.state.startScreen}</Text>
        <Text>returnScreen: {this.state.returnScreen}</Text>
        <Button
          title="Open ModalScreen"
          onPress={() => this.props.navigation.navigate('ModalScreen', {
            startScreen: this.constructor.name,
            setParentState: (state) => this.setState(state)
          })}
        />
        <Button
          title="Open ModalStack"
          onPress={() => this.props.navigation.navigate('ModalStack', {
            startScreen: this.constructor.name,
            setParentState: (state) => this.setState(state)
          })}
        />
      </View>
    )
  }
}

class TabB extends TabA {}
class TabC extends TabA {}

class ModalScreen extends React.Component {
  render () {
    const {
      startScreen,
      setParentState
    } = this.props.navigation.state.params
    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text style={{ fontSize: 20 }}>{this.constructor.name}</Text>
        <Button
          title="Close"
          onPress={() => {
            setParentState({
              startScreen,
              returnScreen: this.constructor.name
            })
            this.props.navigation.goBack()
          }}
        />
      </View>
    )
  }
}


const DismissableStackNavigator = (routes, options) => {
  const StackNav = StackNavigator(routes, options)

  return class DismissableStackNav extends React.Component {
    static router = StackNav.router

    render () {
      const { state, goBack } = this.props.navigation
      const screenProps = {
        ...this.props.screenProps,
        dismissStackNav: () => goBack(state.key)
      }
      return (
        <StackNav
          screenProps={screenProps}
          navigation={this.props.navigation}
        />
      )
    }
  }
}

class ModalStackScreen extends React.Component {
  render () {
    const screenNumber = this.props.navigation.state.params && this.props.navigation.state.params.screenNumber || 0
    const {
      startScreen,
      setParentState
    } = this.props.navigation.state.params
    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text style={{ fontSize: 20 }}>{this.constructor.name + screenNumber}</Text>
        <View style={{
          flexDirection: 'row',
          justifyContent: 'space-between'
        }}>
          <Button
            title={screenNumber === 0 ? "Close" : "Back"}
            onPress={() => this.props.navigation.goBack(null)}
          />
          <Button
            title="Save"
            onPress={() => {
              setParentState({
                startScreen,
                returnScreen: this.constructor.name + screenNumber
              })
              this.props.screenProps.dismissStackNav()
            }}
          />
          <Button
            title="Next"
            onPress={() => this.props.navigation.navigate('ModalStackScreen', {
              screenNumber: screenNumber + 1,
              startScreen,
              setParentState
            })}
          />
        </View>
      </View>
    )
  }
}

const TabNav = TabNavigator(
  {
    TabA: {
      screen: TabA
    },
    TabB: {
      screen: TabB
    },
    TabC: {
      screen: TabC
    }
  }
)

const ModalStack = DismissableStackNavigator(
  {
    ModalStackScreen: {
      screen: ModalStackScreen
    }
  },
  {
    headerMode: 'none'
  }
)

const RootStack = StackNavigator(
  {
    Main: {
      screen: TabNav,
    },
    ModalScreen: {
      screen: ModalScreen,
    },
    ModalStack: {
      screen: ModalStack
    }
  },
  {
    mode: 'modal',
    headerMode: 'none'
  }
)

class App extends React.Component {
  render () {
    return <RootStack />
  }
}

AppRegistry.registerComponent('HelloWorld', () => App)

Previous Answer

This solution is a sledgehammer. It causes the screen of the default tab in the TabNavigator to unmount and then mount again. If the Screen a tab launching the Modal with the StackNavigator passes some callbacks to update it's state, these callbacks will be called when the Screen is unmounted.

The solution was to add key: null to the reset object:

this.props.navigation.dispatch(NavigationActions.reset({
  index: 0,
  key: null,
  actions: [
    NavigationActions.navigate({ routeName: 'App'})
  ]
}))

I was tipped off by this comment.

(FYI - I got here via your comment asking for help.)

After trying almost everything, I've found the solution that worked for me:

  this.props.navigation.popToTop(); // go to the top of the stack
  this.props.navigation.goBack(null); // now show the root screen

As per react navigation doc you can go to any stack by following ways: check the following link for more details React-navigation stack action link

1. import { StackActions, NavigationActions } from 'react-navigation';
         const resetAction = StackActions.reset({
          index: 0,
          actions: [NavigationActions.navigate({ routeName: 'Profile' })],
        });
        this.props.navigation.dispatch(resetAction);
  1. If you have a stack for profile then you can also go through like this check following link nested react navigation

import { NavigationActions } from 'react-navigation';

const navigateAction = NavigationActions.navigate({
  routeName: 'Profile',

  params: {},

  action: NavigationActions.navigate({ routeName: 'SubProfileRoute' }),
});

this.props.navigation.dispatch(navigateAction);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!