无需导航属性即可导航
有时,你需要从无法访问 navigation
对象的地方触发导航操作,例如 Redux 中间件。对于这种情况,你可以使用 导航容器上的 ref
调度导航操作。
¥Sometimes you need to trigger a navigation action from places where you do not have access to the navigation
object, 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
属性,请参阅useNavigation
。ref
的行为有所不同,并且许多特定于屏幕的辅助方法不可用。¥You need to navigate from inside a component without needing to pass the
navigation
prop down, seeuseNavigation
instead. Theref
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.
- Static
- Dynamic
import { createStaticNavigation } from '@react-navigation/native';
import { navigationRef } from './RootNavigation';
/* ... */
const Navigation = createStaticNavigation(RootStack);
export default function App() {
return <Navigation ref={navigationRef} />;
}
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.
- Static
- Dynamic
function navigate(name, params) {
if (navigationRef.isReady()) {
navigationRef.navigate(name, params);
}
}
// Example of usage in any of js modules
//import * as RootNavigation from './path/to/RootNavigation.js';
// ...
// RootNavigation.navigate('ChatScreen', { userName: 'Lucy' });
function Home() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button onPress={() => navigate('Settings', { userName: 'Lucy' })}>
Go to Settings
</Button>
</View>
);
}
function navigate(name, params) {
if (navigationRef.isReady()) {
navigationRef.navigate(name, params);
}
}
// Example of usage in any of js modules
//import * as RootNavigation from './path/to/RootNavigation.js';
// ...
// RootNavigation.navigate('ChatScreen', { userName: 'Lucy' });
function Home() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button onPress={() => navigate('Settings', { userName: 'Lucy' })}>
Go to Settings
</Button>
</View>
);
}
除了 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.
- Static
- Dynamic
const navigationRef = createNavigationContainerRef();
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
}
}
const navigationRef = createNavigationContainerRef();
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
.