Skip to main content
Version: 6.x

无需导航属性即可导航

有时,你需要从无法访问 navigation 属性的位置(例如 Redux 中间件)触发导航操作。对于这种情况,你可以使用 导航容器上的 ref 调度导航操作。

¥Sometimes you need to trigger a navigation action from places where you do not have access to the navigation prop, such as a Redux middleware. For such cases, you can dispatch navigation actions use a ref on the navigation container.

如果出现以下情况,请勿使用 ref

¥Do not use the ref if:

  • 你需要从组件内部导航,而不需要向下传递 navigation 属性,请参阅 useNavigationref 的行为有所不同,并且许多特定于屏幕的辅助方法不可用。

    ¥You need to navigate from inside a component without needing to pass the navigation prop down, see useNavigation instead. The ref behaves differently, and many helper methods specific to screens aren't available.

  • 你需要处理深层链接或通用链接。使用 ref 执行此操作会出现许多边缘情况。有关处理深层链接的更多信息,请参阅 配置链接

    ¥You need to handle deep links or universal links. Doing this with the ref has many edge cases. See configuring links for more information on handling deep linking.

  • 你需要与第三方库集成,例如推送通知、分支等。请参见 用于深度链接的第三方集成

    ¥You need to integrate with third party libraries, such as push notifications, branch etc. See third party integrations for deep linking instead.

如果出现以下情况,请使用 ref

¥Do use the ref if:

  • 你使用 Redux 等状态管理库,需要从中间件分派导航操作。

    ¥You use a state management library such as Redux, where you need to dispatch navigation actions from a middleware.

请注意,通常最好从用户操作(例如按下按钮)触发导航,而不是从 Redux 中间件触发导航。根据用户操作进行导航使应用感觉更具响应性并提供更好的用户体验。因此,在使用 ref 进行导航之前请考虑这一点。ref 是一个应急方案,适用于无法使用现有 API 处理的场景,并且只应在极少数情况下使用。

¥Note that it's usually better to trigger navigation from user actions such as button presses, rather than from a Redux middleware. Navigating on user action makes the app feel more responsive and provides better UX. So consider this before using the ref for navigation. The ref is an escape hatch for scenarios that can't be handled with the existing APIs and should only be used in rare situations.

用法

¥Usage

你可以通过 ref 访问根导航对象并将其传递给我们稍后将用于导航的 RootNavigation

¥You can get access to the root navigation object through a ref and pass it to the RootNavigation which we will later use to navigate.

// App.js

import { NavigationContainer } from '@react-navigation/native';
import { navigationRef } from './RootNavigation';

export default function App() {
return (
<NavigationContainer ref={navigationRef}>{/* ... */}</NavigationContainer>
);
}

在下一步中,我们定义 RootNavigation,这是一个简单的模块,具有调度用户定义的导航操作的功能。

¥In the next step, we define RootNavigation, which is a simple module with functions that dispatch user-defined navigation actions.

// RootNavigation.js

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

export const navigationRef = createNavigationContainerRef();

export function navigate(name, params) {
if (navigationRef.isReady()) {
navigationRef.navigate(name, params);
}
}

// add other navigation functions that you need and export them

然后,在任何 javascript 模块中,导入 RootNavigation 并调用从中导出的函数。你可以在 React 组件之外使用这种方法,事实上,在组件内部使用它也同样有效。

¥Then, in any of your javascript modules, import the RootNavigation and call functions which you exported from it. You may use this approach outside of your React components and, in fact, it works as well when used from within them.

// any js module
import * as RootNavigation from './path/to/RootNavigation.js';

// ...

RootNavigation.navigate('ChatScreen', { userName: 'Lucy' });

除了 navigate 之外,你还可以添加其他导航操作:

¥Apart from navigate, you can add other navigation actions:

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

// ...

export function push(...args) {
if (navigationRef.isReady()) {
navigationRef.dispatch(StackActions.push(...args));
}
}

请注意,需要渲染堆栈导航器来处理此操作。你可能需要检查 嵌套文档 以了解更多详细信息。

¥Note that a stack navigators needs to be rendered to handle this action. You may want to check the docs for nesting for more details.

在编写测试时,你可以模拟导航函数,并断言是否使用正确的参数调用了正确的函数。

¥When writing tests, you may mock the navigation functions, and make assertions on whether the correct functions are called with the correct parameters.

处理初始化

¥Handling initialization

使用此模式时,你需要记住一些事项,以避免应用中的导航失败。

¥When using this pattern, you need to keep few things in mind to avoid navigation from failing in your app.

  • ref 仅在导航容器渲染后设置,在处理深层链接时这可以是异步的

    ¥The ref is set only after the navigation container renders, this can be async when handling deep links

  • 需要渲染导航器才能处理操作,如果没有导航器,ref 将无法准备就绪

    ¥A navigator needs to be rendered to be able to handle actions, the ref won't be ready without a navigator

如果你尝试在不渲染导航器的情况下或在导航器完成安装之前进行导航,它将打印错误并且不执行任何操作。因此,你需要添加额外的检查来决定在应用安装之前要做什么。

¥If you try to navigate without rendering a navigator or before the navigator finishes mounting, it will print an error and do nothing. So you'll need to add an additional check to decide what to do until your app mounts.

例如,考虑以下场景,你在应用中的某个位置有一个屏幕,并且该屏幕在 useEffect/componentDidMount 上调度 redux 操作。你正在中间件中监听此操作,并在收到它时尝试执行导航。这会引发错误,因为此时父导航器尚未完成安装并且尚未准备好。父代的 useEffect/componentDidMount 始终在子代的 useEffect/componentDidMount 之后调用。

¥For an example, consider the following scenario, you have a screen somewhere in the app, and that screen dispatches a redux action on useEffect/componentDidMount. You are listening for this action in your middleware and try to perform navigation when you get it. This will throw an error, because by this time, the parent navigator hasn't finished mounting and isn't ready. Parent's useEffect/componentDidMount is always called after child's useEffect/componentDidMount.

为了避免这种情况,你可以使用参考文献中提供的 isReady() 方法,如上面的示例所示。

¥To avoid this, you can use the isReady() method available on the ref as shown in the above examples.

// RootNavigation.js

import * as React from 'react';

export const navigationRef = createNavigationContainerRef();

export function navigate(name, params) {
if (navigationRef.isReady()) {
// Perform navigation if the react navigation is ready to handle actions
navigationRef.navigate(name, params);
} else {
// You can decide what to do if react navigation is not ready
// You can ignore this, or add these actions to a queue you can call later
}
}

如果不确定是否渲染了导航器,可以调用 navigationRef.current.getRootState(),如果渲染了任何导航器,它将返回有效的状态对象,否则将返回 undefined

¥If you're unsure if a navigator is rendered, you can call navigationRef.current.getRootState(), and it'll return a valid state object if any navigators are rendered, otherwise it will return undefined.