在没有 navigation prop 的情况下导航
有时您需要从无法访问navigation
道具的地方(例如 Redux 中间件)触发导航操作。对于这种情况,您可以使用导航容器上的ref
来调度导航操作。
不要使用ref
,如果
- 您需要从组件内部导航,而无需传递
navigation
道具,请改用useNavigation
。ref
的行为不同,并且许多特定于屏幕的辅助方法不可用。 - 您需要处理深层链接或通用链接。使用
ref
执行此操作有许多边缘情况。有关处理深层链接的更多信息,请参阅配置链接。 - 您需要与第三方库(例如推送通知、分支等)集成。请改用深层链接的第三方集成。
请使用ref
,如果
- 您使用状态管理库(例如 Redux),需要从中间件调度导航操作。
请注意,通常最好从用户操作(例如按钮按下)而不是从 Redux 中间件触发导航。在用户操作时导航会使应用程序感觉更具响应性并提供更好的用户体验。因此,在使用ref
进行导航之前请考虑这一点。ref
是用于无法使用现有 API 处理的场景的应急措施,应仅在极少数情况下使用。
用法
您可以通过ref
访问根导航对象,并将其传递给RootNavigation
,我们将在稍后使用它进行导航。
// App.js
import { NavigationContainer } from '@react-navigation/native';
import { navigationRef } from './RootNavigation';
export default function App() {
return (
<NavigationContainer ref={navigationRef}>{/* ... */}</NavigationContainer>
);
}
在下一步中,我们定义RootNavigation
,它是一个简单的模块,包含调度用户定义的导航操作的函数。
// 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 组件之外使用这种方法,事实上,它在从组件内部使用时也能正常工作。
// any js module
import * as RootNavigation from './path/to/RootNavigation.js';
// ...
RootNavigation.navigate('ChatScreen', { userName: 'Lucy' });
除了navigate
之外,您还可以添加其他导航操作
import { StackActions } from '@react-navigation/native';
// ...
export function push(...args) {
if (navigationRef.isReady()) {
navigationRef.dispatch(StackActions.push(...args));
}
}
请注意,需要渲染一个堆栈导航器来处理此操作。您可能需要查看嵌套文档以获取更多详细信息。
在编写测试时,您可以模拟导航函数,并断言是否使用正确的参数调用了正确的函数。
处理初始化
使用此模式时,您需要牢记一些事项,以避免导航在您的应用程序中失败。
ref
仅在导航容器渲染后设置,在处理深层链接时这可能是异步的。- 需要渲染一个导航器才能处理操作,如果没有导航器,
ref
将无法准备就绪。
如果您尝试在渲染导航器之前或导航器完成挂载之前导航,它将打印错误并不会执行任何操作。因此,您需要添加额外的检查来决定在您的应用程序挂载之前要执行的操作。
例如,考虑以下场景,您在应用程序中的某个地方有一个屏幕,该屏幕在useEffect
/componentDidMount
上分派了一个 Redux 操作。您在中间件中监听此操作,并在收到它时尝试执行导航。这将抛出错误,因为此时,父导航器尚未完成挂载,尚未准备好。父级的useEffect
/componentDidMount
始终在子级的useEffect
/componentDidMount
之后调用。
为了避免这种情况,您可以使用isReady()
方法,该方法在上述示例中在 ref 上可用。
// 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
。