React Navigation 5 - 在导航到另一个选项卡之前从另一个堆栈重置堆栈(类似于 popToTop()) [英] React Navigation 5 - Reset a stack (similar to popToTop()) from another stack in a different tab before navigating to it

查看:33
本文介绍了React Navigation 5 - 在导航到另一个选项卡之前从另一个堆栈重置堆栈(类似于 popToTop())的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设标签导航器中有两个堆栈屏幕:

<块引用>

  1. 标签 A ->相机
  2. 标签 B ->简介

在配置文件屏幕中,其堆栈中还有其他相同类型(配置文件")(具有不同参数)的屏幕.现在,如果您在相机"中屏幕并执行:

 navigation.navigate("Profile", { screen: "Profile", params });

您将导航到个人资料"screen 并且这些参数将被发送到堆栈中的最后一个屏幕.如果我想导航到传递参数的堆栈的根,我该怎么做?

我尝试过:

//在个人资料屏幕中useEffect(() => {如果 (navigation.canGoBack())导航.popToTop();//回到栈根显示参数(参数);}, [参数])

但是有了这个,showParams"操作不在根中执行,我也没有从相机"直接导航到堆栈的根.屏幕.

我想我必须在导航之前在相机"屏幕中执行以下操作:

 navigation.dispatch(CommonActions.reset({//一些东西}));navigation.navigate(Profile", { screen: Profile", params });

但我找不到任何方法来实现我的目标.

有什么想法吗?谢谢.

更新 - 我的导航系统

堆栈(这里我定义了多个堆栈:HomeStacks"、SearchStacks"、ProfileStacks"...)

const Stack = createStackNavigator();导出函数 ProfileStacks() { <------ 在这个堆栈上我做 .push()返回 (<堆栈导航器initialRouteName=个人资料"><Stack.Screen name="Profile";儿童={个人资料}/><Stack.Screen name="EditProfile";children={EditProfile}/></Stack.Navigator>);}...

底部标签导航器

<标签屏幕名称=相机"组件={CameraPlaceholder}听众={({导航}) =>({tabPress: (事件) =>{event.preventDefault();navigation.navigate(CameraModal");},})}/><标签屏幕名称=个人资料"组件={ProfileStacks}/></Tab.Navigator>

ROOT STACK NAVIGATOR(应用的主导航器)

在这个堆栈中,我实现了身份验证流程,并且还声明了一些额外的堆栈(仅用于外观目的).

导出默认函数RootNavigator(props) {/*这个导航器是使用受保护的路线"模式*/const { isUserLoggedIn } = props;const RootStack = createStackNavigator();返回 (<RootStack.Navigator>{是用户登录?(<><RootStack.Screenname =BottomTabNavigator"组件={BottomTabNavigator}/><RootStack.Screen名称=CameraModal"组件={相机}/></>) : (<><RootStack.Screen name="SignIn";组件={登录}/><RootStack.Screen名称=注册"组件={注册}/><RootStack.Screen名称=忘记密码";组件={忘记密码}/></>)}</RootStack.Navigator>);

我见过的相关问题

如何使用 React Navigation 5.x

在不同的选项卡中重置堆栈

https://github.com/react-navigation/react-navigation/issues/6639

https://github.com/react-navigation/react-navigation/issues/8988

这是我的个人资料标签的导航数据

 对象 {key":Profile-Ty4St1skrxoven-jkZUsx",名称":个人资料",参数":未定义,状态":对象{指数":1,key":stack-8nWDnwDJZRK8iDuJok7Hj",路由名称":数组 [个人资料",编辑配置文件",],路线":数组 [目的 {key":Profile-m0GQkvNk5RjAhGABvOy9n",名称":个人资料",参数":未定义,},目的 {key":Profile-tZAEmSU0eEo1Nt7XC09t1",名称":个人资料",参数":对象{otherUserData":对象{用户名":jeffbezos",},帖子":空,},},],陈旧":假,类型":堆栈",},},],陈旧":假,类型":标签",},

我只需要从堆栈配置文件"中弹出第二条路由;这是在选项卡配置文件"中从我应用的另一个标签,然后导航到此屏幕.

解决方案

经过几个小时的研究,我找到了解决方案.这不是最好的,但它适用于我的用例,它肯定适用于其他人.

我试图实现的是重置配置文件"的路由.位于堆栈导航器中的屏幕,而堆栈导航器又位于我当前堆栈屏幕所在的选项卡导航器的另一个选项卡中.这听起来有点令人困惑,但它基本上类似于上传照片时在 Instagram 上发生的情况.

如果在 Instagram 中您从主屏幕导航到其他用户的个人资料,然后将照片上传到您的帐户,您将看到如何从发布您的照片"屏幕到主页选项卡中的堆栈导航器的根目录,即提要.

在我的用例中,我正在做类似的事情,我可以从我自己的个人资料导航到其他用户的个人资料,并且照片会上传到此屏幕中,带有进度条.

从一开始我就想使用navigation.popToTop(),但是我一直无法获得我想要的结果,因为正如我之前在问题中评论过的,参数(包含帖子的)是丢失的.所以我别无选择,只能从我的发布照片"中模拟这种行为.屏幕.

我遵循的步骤如下:

<块引用>

  1. 作为我的发布照片"screen 与我的个人资料"共享导航;屏幕,通过选项卡导航器(这是显而易见的,因为如果不是那样我就不能做 navigation.navigate()),我已经按照导航路径从这里到配置文件选项卡的堆栈导航器,然后我有尝试获取其密钥和路由.

<块引用>

  1. 如果我找到了当前的键和路径,这意味着堆栈导航器已安装(在我的情况下,该选项卡对我的所有页面进行了延迟初始化,这就是我所说的尝试获取"的原因).因此,有必要应用第 3 步和第 4 步.

<块引用>

  1. 模拟 navigation.popToTop() 将路由的大小减少到 1(注意堆栈导航器的根是路由"数组第一个位置的项目)

<块引用>

  1. 使用导航 API 通过配置文件的堆栈导航器分派重置操作.

<块引用>

  1. 最后一步,导航到堆栈屏幕,通常将照片作为参数传递.

代码如下:

 const resetProfileStackNavigator = () =>{const currentNavigationState = navigation.dangerouslyGetState();//找到底部导航器for (let i = 0; i < currentNavigationState?.routes?.length; i++) {if (currentNavigationState.routes[i].name === "BottomTabNavigator") {//获取它的状态const bottomNavigationState = currentNavigationState.routes[i].state;//找到配置文件选项卡for (let j = 0; j 

Pd:我知道有一些适用的重构,但我更喜欢以这种方式显示代码,以便我上面讨论的步骤清晰可见.

如果任何读过这篇文章的人能够将这段代码概括成一个使用 ES6 的通用函数,请将其作为答案,因为它对我和其他用户非常有用.

Suppose two stack screens in a Tab Navigator:

  1. Tab A -> Camera
  2. Tab B -> Profile

In the profile screen, there are other screens of the same type ("Profile") pushed (with different params) in its stack. Now, if you are in the "Camera" screen and do:

    navigation.navigate("Profile", { screen: "Profile", params });

You will navigate to the "Profile" screen and those params will be sent to the last screen in the stack. What can I do if I want to navigate to the root of the stack passing the params?

I have tried with:

   // In the profile screen
   useEffect(() => {
       if (navigation.canGoBack())
            navigation.popToTop(); // Go back to the root of the stack

       showParams(params);
   }, [params])

but with this, the "showParams" operation isn't performed in the root, and I am not navigating directly to the root of the stack from the "Camera" screen.

I think I have to do something like this in the Camera screen before navigating:

  navigation.dispatch(
        CommonActions.reset({
          // some stuff
        })
  );

  navigation.navigate("Profile", { screen: "Profile", params });

But I can't find any way to achieve my goal.

Any ideas? Thank you.

UPDATE - My navigation system

STACKS (Here I define multiple stacks: "HomeStacks", "SearchStacks", "ProfileStacks" ...)

const Stack = createStackNavigator();

export function ProfileStacks() { <------ Over this stack I do .push()
  return (
    <Stack.Navigator
      initialRouteName="Profile"
    >
      <Stack.Screen name="Profile" children={Profile} />
      <Stack.Screen name="EditProfile" children={EditProfile} />
    </Stack.Navigator>
  );
}

...

BOTTOM TAB NAVIGATOR

<Tab.Navigator>
  <Tab.Screen
    name="Camera"
    component={CameraPlaceholder}
    listeners={({ navigation }) => ({
      tabPress: (event) => {
        event.preventDefault();
        navigation.navigate("CameraModal");
      },
    })}
  />

  <Tab.Screen
    name="Profile"
    component={ProfileStacks}
  />
</Tab.Navigator>

ROOT STACK NAVIGATOR (The main navigator of the app)

In this stack I implement the authentication flow and also, I declare some extra stacks (just for look-and-feel purposes).

export default function RootNavigator(props) {
  /* 
    This navigator is implemented using the
    'Protected Routes' pattern
  */
  const { isUserLoggedIn } = props;

  const RootStack = createStackNavigator();

  return (
    <RootStack.Navigator>
      {isUserLoggedIn ? (
        <>
          <RootStack.Screen
            name="BottomTabNavigator"
            component={BottomTabNavigator}
          />

          <RootStack.Screen
            name="CameraModal"
            component={Camera}
          />
        </>
      ) : (
        <>
          <RootStack.Screen name="SignIn" component={SignIn} />

          <RootStack.Screen
            name="SignUp"
            component={SignUp}
          />

          <RootStack.Screen
            name="ForgotPassword"
            component={ForgotPassword}
          />
        </>
      )}
    </RootStack.Navigator>
  );

Related problems I have seen

How to reset a Stack in a different Tab using React Navigation 5.x

https://github.com/react-navigation/react-navigation/issues/6639

https://github.com/react-navigation/react-navigation/issues/8988

This is my Profile tab's navigation data

  Object {
        "key": "Profile-Ty4St1skrxoven-jkZUsx",
        "name": "Profile",
        "params": undefined,
        "state": Object {
          "index": 1,
          "key": "stack-8nWDnwDJZRK8iDuJok7Hj",
          "routeNames": Array [
            "Profile",
            "EditProfile",
          ],
          "routes": Array [
            Object {
              "key": "Profile-m0GQkvNk5RjAhGABvOy9n",
              "name": "Profile",
              "params": undefined,
            },
            Object {
              "key": "Profile-tZAEmSU0eEo1Nt7XC09t1",
              "name": "Profile",
              "params": Object {
                "otherUserData": Object {
                  "username": "jeffbezos",
                },
                "post": null,
              },
            },
          ],
          "stale": false,
          "type": "stack",
        },
      },
    ],
    "stale": false,
    "type": "tab",
  },

I just need to pop the second route from the stack "Profile" which is in the tab "Profile" from another Tab of my app, and then navigate to this screen.

解决方案

After a few hours studying the problem I have found a solution. It's not the best but it works for my use case and it surely works for other people's.

What I have tried to achieve is to reset the routes of the "Profile" screen that is in a stack navigator that in turn is in another tab of the tab navigator in which my current stack screen is. It sounds somewhat confusing, but it is basically something similar to what happens on instagram when you upload a photo.

If in Instagram you navigate to other users profiles from the Home screen and then upload a photo to your account, you will see how you go from the "publish your photo" screen to the root of the stack navigator that is in the Home tab, the feed.

In my use case, I am doing something similar, I can navigate to other users profiles from my own profile, and the photos are uploaded in this screen, with a progress bar.

From the beginning I had in mind to use navigation.popToTop (), but I have not been able to obtain the result I wanted, because as I have commented previously in the question, the parameters (that contained the post) were lost. So I have no choice but to simulate this behavior from my "publish photo" screen.

The steps I have followed are as follows:

  1. As my "publish photo" screen shares the navigation with my "profile" screen, through the tab navigator (which is obvious, since if it were not like that I could not do the navigation.navigate()), I have followed the navigation path from this to the Stack Navigator of the Profile Tab and then I have tried to take both its key and its routes.

  1. In case I have found the current key and paths, that means the stack navigator is mounted (in my case, the tab does a lazy initialization of all my pages, that's why I speak of "trying to take"). So it will be necessary to apply steps 3 and 4.

  1. Simulate the navigation.popToTop() reducing the size of the routes to 1 (note that the root of the stack navigator is the item in the first position of the "routes" array)

  1. Dispatch the reset operation over the profile's stack navigator using the navigation API.

  1. The final step, navigate to the stack screen normally passing the photo as param.

Here is the code:

  const resetProfileStackNavigator = () => {
      const currentNavigationState = navigation.dangerouslyGetState();

      // Find the bottom navigator
      for (let i = 0; i < currentNavigationState?.routes?.length; i++) {
        if (currentNavigationState.routes[i].name === "BottomTabNavigator") {
          // Get its state
          const bottomNavigationState = currentNavigationState.routes[i].state;

          // Find the profile tab
          for (let j = 0; j < bottomNavigationState?.routes?.length; j++) {
            if (bottomNavigationState.routes[j].name === "Profile") {
              // Get its state
              const profileTabState = bottomNavigationState.routes[j].state;

              // Get the key of the profile tab's stack navigator
              var targetKey = profileTabState?.key;
              var targetCurrentRoutes = profileTabState?.routes;

              break;
            }
          }
          break;
        }
      }

      // Reset the profile tab's stack navigator if it exists and has more than one stacked screen
      if (targetKey && targetCurrentRoutes?.length > 1) {
        // Set a new size for its current routes array, which is faster than Array.splice to mutate
        targetCurrentRoutes.length = 1; // This simulates the navigation.popToTop()

        navigation.dispatch({
          ...CommonActions.reset({
            routes: targetCurrentRoutes, // It is necessary to copy the existing root route, with the same key, to avoid the component unmounting
          }),
          target: targetKey,
        });
      }
 }


  /*
    Maybe, the stack navigator of the profile tab exists and has changed from its initial state...
    In this situation, we will have to find the key of this stack navigator, which is also 
    nested in the same tab navigator in which this stack screen is.
  */
  resetProfileStackNavigator();

  // Finally, navigate to the profile stack screen and pass the post as param
  navigation.navigate("Profile", {
    screen: "Profile",
    params: {
      post,
    },
  });

Pd: I know there are some applicable refactorings, but I prefer to display the code this way so that the steps I discussed above are clearly visible.

If anyone who has read this manages to generalize this segment of code into a generic function using ES6, please leave it as an answer, as it can be very useful for me and for other users.

这篇关于React Navigation 5 - 在导航到另一个选项卡之前从另一个堆栈重置堆栈(类似于 popToTop())的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆