重置主屏幕的导航栈(React Navigation和React Native)

115
我遇到了与React Navigation和React Native导航相关的问题。问题在于重置导航并返回主屏幕。
我在DrawerNavigator中构建了一个StackNavigator,并且主屏幕和其他屏幕之间的导航正常工作。但是问题是,导航堆栈不断增长。我不确定如何从堆栈中删除屏幕。
例如,当从主屏幕转到设置屏幕,然后再转到输入屏幕,最后再次回到主屏幕时,主屏幕在堆栈中出现两次。使用返回按钮无法退出应用程序,而是返回输入屏幕。
当再次选择主页按钮时,重置堆栈将非常好,但我不知道如何做到这一点。此处有人尝试帮助另一个遇到类似问题的人,但解决方案对我没用。
const Stack = StackNavigator({
  Home: {
    screen: Home
  },
  Entry: {
    screen: Entry
  },
  Settings: {
    screen: Settings
  }
})

export const Drawer = DrawerNavigator({
  Home: {
    screen: Stack
  }},
  {
    contentComponent: HamburgerMenu
  }
)

这是抽屉式屏幕的简单示例

export default class HamburgerMenu extends Component {
  render () {
    return <ScrollView>
      <Icon.Button
        name={'home'}
        borderRadius={0}
        size={25}
        onPress={() => { this.props.navigation.navigate('Home')}}>
        <Text>{I18n.t('home')}</Text>
      </Icon.Button>

      <Icon.Button
        name={'settings'}
        borderRadius={0}
        size={25}
        onPress={() => { this.props.navigation.navigate('Settings')}}>
        <Text>{I18n.t('settings')}</Text>
      </Icon.Button>

      <Icon.Button
        name={'entry'}
        borderRadius={0}
        size={25}
        onPress={() => { this.props.navigation.navigate('Entry')}}>
        <Text>{I18n.t('entry')}</Text>
      </Icon.Button>
    </ScrollView>
  }
}

我希望你能帮助我。这是导航的重要部分,如果有解决方案就太好了!

15个回答

115

React Navigation 5.x, 6.x

import { CommonActions } from '@react-navigation/native';

navigation.dispatch(
  CommonActions.reset({
    index: 1,
    routes: [
      { name: 'Home' },
      {
        name: 'Profile',
        params: { user: 'jane' },
      },
    ],
  })
);

可以在Snack中获取。


2
仍然无法正常工作,当我按下返回按钮时它会回到先前的页面。 - Akshay I
12
这个答案应该在最上面,我浪费了大约30分钟的时间去寻找它。🙏 - Vishwesh Jainkuniya
1
这将适用于5.x版本。 - Luiey
1
适用于React Navigation 5.10。 - Arafat Zahan
2
这对我有用,谢谢!为什么只使用CommonActions.reset时不起作用? - Victor Sena de Lima Attar
显示剩余2条评论

74

这是我做事的方式:

reset(){
    return this.props
               .navigation
               .dispatch(NavigationActions.reset(
                 {
                    index: 0,
                    actions: [
                      NavigationActions.navigate({ routeName: 'Menu'})
                    ]
                  }));
  }

至少将'Menu'替换为'Home'。您可能还希望根据自己的实现调整this.props.navigation。

在版本>2中,请按照以下步骤进行操作:

import { NavigationActions, StackActions } from 'react-navigation';
        const resetAction = StackActions.reset({
                index: 0,
                actions: [NavigationActions.navigate({ routeName: 'MainActivity' })],
            });

this.props.navigation.dispatch(resetAction); 

33
产生一个令大多数QA不接受的恶劣动画,供参考。 - Oliver Dixon
1
在这种情况下如何发送导航参数? - Ashish
2
为了避免不良动画效果,我不得不根据这个答案完全禁用所有过渡动画。 - Kes115
我很惊讶这是被接受的答案,考虑到动画的问题。我也遇到了这个问题,但会去看看你们建议的链接。 - SwimmingG
3
我正在使用React Navigation v2的API,reset API已经移动到StackActions(https://v2.reactnavigation.org/docs/en/stack-actions.html),但是它对我不起作用,我正在尝试按照链接中给出的方式进行。 - Sadanand
显示剩余4条评论

46

在使用@react-navigationBashirpour的答案时,我发现了一种方法。然而,当您尝试在函数组件中使用已经包含导航props时,这里有一种不错的方法来编写重置堆栈操作:

props.navigation.reset({
     index: 0,
     routes: [{ name: 'Dashboard' }]
})

4
尝试了很多不同的方法,但对于函数组件而言,最好的解决方案能够拯救我的一天。 - haresh
1
我想这是最好的方法。 - Kvlknctk
1
我在堆栈中的路由的componentWillUnmount上清除了一个timeInterval,但似乎并没有起作用。组件从未进入“卸载”状态? - user2078023
2
似乎适用于选项卡导航,但在屏幕切换时会产生明显的延迟。 - augsteyer
函数组件应该使用 navigation.reset 而不是 props.navigation.reset - cmcodes
如果需要的话,您也可以从容器初始化的导航引用中运行此内容。除非在组件树之外以命令方式工作,否则通常不建议使用ref而不是使用prop,因为无法支持您拥有的任何子导航器上的所有方法。但是,由于您正在完全清除状态,只要它对isReady()回答true,主容器就不会有任何问题。干杯! - Mike Hardy

27

我是这样做的:

import { NavigationActions } from 'react-navigation'

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

重要的部分是key: null

在从子导航器导航到父导航器时,使用key: null清除堆栈。

如果您遇到此错误,请执行此操作:

enter image description here

对于动画,我使用

// https://github.com/oblador/react-native-animatable
import * as Animatable from 'react-native-animatable'

我只是自己控制所有的动画。通过用<Animatable.View>将其包裹在任何你想要的组件上,你可以使它们生效。


6
谢谢!“key: null”是我所缺少的 :) - SubmittedDenied
上面展示的代码正在分派重置堆栈的操作,因此它会在您希望触发该操作的位置执行。例如,您可以将 NavigationActions 导入到您的操作创建者文件中,并在某些操作通过时重置堆栈。 - agm1984

20

对于最新的react-navigation版本,您应该使用StackActions来重置堆栈,以下是一段代码:

// import the following
import { NavigationActions, StackActions } from 'react-navigation'

// at some point in your code
resetStack = () => {
 this.props
   .navigation
   .dispatch(StackActions.reset({
     index: 0,
     actions: [
       NavigationActions.navigate({
         routeName: 'Home',
         params: { someParams: 'parameters goes here...' },
       }),
     ],
   }))
}

如果他想进入“设置”屏幕怎么办? - Avinash Raj
@AvinashRaj,我不确定我是否完全理解了你的问题,但如果我理解了,只需在我发布的代码片段中将routeName:'Settings'替换为routeName:'Home'。 希望能有所帮助。 - Gustavo Garcia
我通过添加另一个操作项使其工作。你知道如何设置抽屉项目的焦点吗?因为通过这种方式导航后,焦点仍然在默认的抽屉项目上。 - Avinash Raj
嗨,我有一个BottomNavigation,reset: 0替换了我的BottomNavigation,你能帮忙吗?我不想这样,我想在我的底部导航后从新的屏幕重置。 - famfamfam

7

在React Navigation 5.x版本中

您可以在此版本中使用StackActions.replace

import { StackActions } from '@react-navigation/native';


navigation.dispatch(
    StackActions.replace('Home', { test: 'Test Params' })
)

Full Example: (Available in Snack)

import * as React from 'react';
import { View, Button, Text } from 'react-native';
import { NavigationContainer, StackActions } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

function SplashScreen({ navigation }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text style={{fontSize:25,marginBottom:25}} >SPLASH SCREEN!</Text>
      <Button
        title="Replace (RESET) with Home"
        onPress={() =>
          navigation.dispatch(
            StackActions.replace('Home', { test: 'Test Params' })
          )
        }
      />
      <View style={{margin:10}}/>
      <Button
        title="Push Home on the stack"
        onPress={() =>
          navigation.dispatch(StackActions.push('Home', { test: 'Test Params' }))
        }
      />
    </View>
  );
}

function HomeScreen({ navigation, route }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text style={{fontSize:25,marginBottom:25}}>Home Screen!</Text>

      <Text style={{margin:10}}>{route.params.test}</Text>

      <Button
        title="Push same screen on the stack"
        onPress={() => navigation.dispatch(StackActions.pop(1))} 
      /> 
      <View style={{margin:10}}/>
      <Button
        title="Pop one screen from stack" 
        onPress={() =>
          navigation.dispatch(StackActions.push('Home', { test: 'Test Params' }))
        }
      />
      <View style={{margin:10}}/>
      <Button
        title="Pop to top" 
        onPress={() => navigation.dispatch(StackActions.popToTop())}
      />
    </View>
  );
}

const Stack = createStackNavigator();

export default function App() { 
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Splash" component={SplashScreen} />
        <Stack.Screen name="Home" component={HomeScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}


3

NavigationActions.reset()似乎是更好的解决方案。我在使用它时遇到问题的一个问题是选项卡按钮。即使我在组件中明确关闭了选项卡,选项卡仍然会显示出来。如果我使用navigation.navigate()而不是通过reset()来做这个操作,那么它将正常工作。

SomeComponentScreen.navigationOptions = {
  header: null
};

一个解决这个烦恼的方法是连续调用多个 navigate 语句。
      navigation.goBack();   // this would pop current item in stack
      navigation.navigate({
        routeName: 'SomeOtherComponent'
      });

3

要使用“后退”功能,您需要找到与路径相关联的唯一键。在导航减速器内,您可以搜索现有状态以查找堆栈上使用该路径的第一个路由,获取其键并将其传递给“后退”功能。然后,“后退”将导航到您提供的路径之前的屏幕。

  let key;

  if (action.payload) {
    // find first key associated with the route
    const route = action.payload;

    const routeObj = state.routes.find( (r) => r.routeName === route );

    if (routeObj) {
      key = { key: routeObj.key };
    }
  }

  return AppNavigator.router.getStateForAction( NavigationActions.back( key ), state );

2
只需混合上述两个解决方案就可以正常工作,基本上我们必须使用StackActions和key:null。如果不使用StackActions,则会抛出一些错误。
import { NavigationActions, StackActions } from 'react-navigation';
const resetHandler = () => {
        props.navigation.dispatch(StackActions.reset({
            index: 0,
            key: null,
            actions: [NavigationActions.navigate({ routeName: 'PatientDetails' })]
        }))
    };

嗨,我有一个BottomNavigation,reset: 0替换了我的BottomNavigation,你能帮忙吗?我不想这样,我想在底部导航之后从新的屏幕重置。 - famfamfam

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接