跳到主要内容
版本: 7.x

不使用 navigation prop 进行导航

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

不要使用 ref 如果

  • 您需要在组件内部导航,而无需向下传递 navigation prop,请参阅 useNavigation 代替。 ref 的行为有所不同,并且许多特定于屏幕的辅助方法不可用。
  • 您需要处理深度链接或通用链接。使用 ref 执行此操作有很多边缘情况。有关处理深度链接的更多信息,请参阅配置链接
  • 您需要与第三方库集成,例如推送通知、branch 等。请参阅深度链接的第三方集成代替。

可以使用 ref 如果

  • 您使用状态管理库(如 Redux),您需要从中调度来自中间件的导航操作。

请注意,通常最好从用户操作(例如按钮按下)而不是从 Redux 中间件触发导航。在用户操作时导航使应用程序感觉更灵敏并提供更好的用户体验。因此,在使用 ref 进行导航之前请考虑这一点。 ref 是现有 API 无法处理的情况的应急方案,应仅在极少数情况下使用。

用法

您可以通过 ref 访问根导航对象,并将其传递给 RootNavigation,我们稍后将使用它进行导航。

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

/* ... */

const Navigation = createStaticNavigation(RootStack);

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

在下一步中,我们定义 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 组件外部使用此方法,事实上,从组件内部使用时效果也很好。

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>
);
}
Snack 上尝试

除了 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 之后调用。

为了避免这种情况,您可以使用 ref 上可用的 isReady() 方法,如上面的示例所示。

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
}
}
Snack 上尝试

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