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

故障排除

本节尝试概述用户在初次习惯使用 React Navigation 时经常遇到的问题。这些问题可能与 React Navigation 本身相关,也可能不相关。

在排除问题之前,请确保您已升级到软件包的最新可用版本。您可以通过重新安装软件包来安装最新版本(例如 npm install package-name)。

更新到最新版本后,我收到错误“Unable to resolve module”

这可能是由 3 个原因造成的

Metro bundler 的陈旧缓存

如果模块指向本地文件(即模块名称以 ./ 开头),则可能是由于陈旧缓存。要解决此问题,请尝试以下解决方案。

如果您正在使用 Expo,请运行

expo start -c

如果您没有使用 Expo,请运行

npx react-native start --reset-cache

如果这不起作用,您也可以尝试以下操作

rm -rf $TMPDIR/metro-bundler-cache-*

缺少对等依赖

如果模块指向 npm 包(即模块名称不以 ./ 开头),则可能是由于缺少依赖项。要解决此问题,请在您的项目中安装依赖项

npm install name-of-the-module

有时,这甚至可能是由于安装损坏造成的。如果清除缓存不起作用,请尝试删除您的 node_modules 文件夹并再次运行 npm install

metro 配置中缺少扩展名

有时错误可能看起来像这样

Error: While trying to resolve module "@react-navigation/native" from file "/path/to/src/App.js", the package "/path/to/node_modules/@react-navigation/native/package.json" was successfully found. However, this package itself specifies a "main" module field that could not be resolved ("/path/to/node_modules/@react-navigation/native/src/index.tsx"

如果您对 metro 有自定义配置并且没有将 tstsx 指定为有效扩展名,则可能会发生这种情况。这些扩展名存在于默认配置中。要检查这是否是问题所在,请在您的项目中查找 metro.config.js 文件,并检查您是否指定了 sourceExts 选项。它至少应具有以下配置

sourceExts: ['js', 'json', 'ts', 'tsx'];

如果缺少这些扩展名,请添加它们,然后按照上面部分所示清除 metro 缓存。

更新到最新版本后,我收到错误“@react-navigation/xxx/xxx.tsx 中出现 SyntaxError”或“SyntaxError: /xxx/@react-navigation/xxx/xxx.tsx: Unexpected token”

如果您的 @react-native/babel-preset 包版本较旧,则可能会发生这种情况。尝试将其升级到最新版本。

npm install --save-dev @react-native/babel-preset

如果您安装了 @babel/core,也请将其升级到最新版本。

npm install --save-dev @babel/core

如果升级软件包没有帮助,您也可以尝试删除您的 node_modules,然后锁定文件并重新安装您的依赖项。

如果您使用 npm

rm -rf node_modules
rm package-lock.json
npm install

如果您使用 yarn

rm -rf node_modules
rm yarn.lock
yarn
警告

通常不建议删除 lockfile,因为它可能会将您的依赖项升级到尚未在您的项目中测试过的版本。因此,仅将其用作最后的手段。

升级或重新安装软件包后,您还应按照页面前面部分的说明清除 Metro bundler 的缓存。

使用 TypeScript 时,我收到错误“Module '[...]' has no exported member 'xxx'”

如果您的项目中 TypeScript 版本较旧,则可能会发生这种情况。您可以尝试升级它

npm install --save-dev typescript

我收到错误“null is not an object (evaluating 'RNGestureHandlerModule.default.Direction')”

如果您有一个裸 React Native 项目,并且未链接 react-native-gesture-handler 库,则可能会发生这种情况以及一些类似错误。

从 React Native 0.60 开始,链接是自动的,因此如果您手动链接了库,请首先取消链接

react-native unlink react-native-gesture-handler

如果您在 iOS 上进行测试并且使用 Mac,请确保您已在 ios/ 文件夹中运行 pod install

cd ios
pod install
cd ..

现在重新构建应用程序并在您的设备或模拟器上进行测试。

我收到错误“requireNativeComponent: "RNCSafeAreaProvider" was not found in the UIManager”

如果您有一个裸 React Native 项目,并且未链接 react-native-safe-area-context 库,则可能会发生这种情况以及一些类似错误。

从 React Native 0.60 开始,链接是自动的,因此如果您手动链接了库,请首先取消链接

react-native unlink react-native-safe-area-context

如果您在 iOS 上进行测试并且使用 Mac,请确保您已在 ios/ 文件夹中运行 pod install

cd ios
pod install
cd ..

现在重新构建应用程序并在您的设备或模拟器上进行测试。

我收到错误“Tried to register two views with the same name RNCSafeAreaProvider”

如果您安装了多个版本的 react-native-safe-area-context,则可能会发生这种情况。

如果您使用的是 Expo managed workflow,则可能是您安装了不兼容的版本。要安装正确的版本,请运行

npx expo install react-native-safe-area-context

如果它没有修复错误,或者您没有使用 Expo managed workflow,则需要检查哪个包依赖于不同版本的 react-native-safe-area-context

如果您使用 yarn,请运行

yarn why react-native-safe-area-context

如果您使用 npm,请运行

npm ls react-native-safe-area-context

这将告诉您您使用的包是否依赖于 react-native-safe-area-context。如果是第三方包,您应该在相关 repo 的 issue tracker 上打开一个 issue,解释问题。通常,对于库,包含本机代码的依赖项应在 peerDependencies 而不是 dependencies 中定义,以避免此类问题。

如果它已经在 peerDependencies 中而不是在 dependencies 中,并且您使用 npm,则可能是由于为包定义了不兼容的版本范围。库的作者将需要在这种情况下放宽版本范围,以允许安装更广泛的版本。

如果您使用 yarn,您也可以使用 resolutions 临时覆盖正在安装的版本。在您的 package.json 中添加以下内容

"resolutions": {
"react-native-safe-area-context": "<version you want to use>"
}

然后运行

yarn

如果您在 iOS 上并且没有使用 Expo managed workflow,也请运行

cd ios
pod install
cd ..

现在重新构建应用程序并在您的设备或模拟器上进行测试。

添加 View 后屏幕上看不到任何内容

如果您将容器包装在 View 中,请确保 View 使用 flex: 1 拉伸以填充容器

import * as React from 'react';
import { View } from 'react-native';
import { createStaticNavigation } from '@react-navigation/native';

/* ... */

const Navigation = createStaticNavigation(RootStack);

export default function App() {
return (
<View style={{ flex: 1 }}>
<Navigation />
</View>
);
}

我收到警告“在导航状态中发现了非序列化值”

如果您在参数中传递了非序列化值(例如类实例、函数等),则可能会发生这种情况。React Navigation 在这种情况下会警告您,因为这可能会破坏其他功能,例如 状态持久化深度链接 等。

以下是一些在参数中传递函数的用例示例

  • 要传递要在 header button 中使用的回调。可以使用 navigation.setOptions 代替实现此目的。有关示例,请参阅 header buttons 指南
  • 要将回调传递到下一个屏幕,它可以调用该回调以传递一些数据返回。您通常可以使用 popTo 代替实现它。有关示例,请参阅 将参数传递到上一个屏幕
  • 要将复杂数据传递到另一个屏幕。您可以将该复杂数据存储在其他地方(例如全局存储),而不是传递数据 params,而是传递一个 id。然后屏幕可以使用该 id 从全局存储中获取数据。请参阅 参数中应该包含什么
  • 从父屏幕到子屏幕传递数据、回调等。您可以使用 React Context,或传递子回调来传递这些数据,而不是使用参数。请参阅 传递其他 props

如果您不使用状态持久化或深度链接到接受参数中函数的屏幕,则警告不会影响您,您可以安全地忽略它。要忽略警告,您可以使用 LogBox.ignoreLogs

示例

import { LogBox } from 'react-native';

LogBox.ignoreLogs([
'Non-serializable values were found in the navigation state',
]);

我收到错误“Invalid hook call. Hooks can only be called inside of the body of a function component”

当您将 React 组件传递给接受返回 react 元素的函数的选项时,可能会发生这种情况。例如,原生堆栈导航器中的 headerTitle 选项 期望一个返回 react 元素的函数

const Stack = createNativeStackNavigator({
screens: {
Home: {
screen: Home,
options: {
headerTitle: (props) => <MyTitle {...props} />,
},
},
},
});

如果您在此处直接传递一个函数,则在使用 hooks 时会收到此错误

const Stack = createNativeStackNavigator({
screens: {
Home: {
screen: Home,
options: {
// This is not correct
headerTitle: MyTitle,
},
},
},
});

这同样适用于其他选项,如 headerLeftheaderRighttabBarIcon 等,以及 props,如 tabBardrawerContent 等。

屏幕在导航期间卸载/重新挂载

有时您可能会注意到您的屏幕在导航时卸载/重新挂载,或者您的本地组件状态或导航状态重置。如果您在渲染期间创建 React 组件,则可能会发生这种情况。

最简单的示例类似于以下内容

function App() {
return (
<Stack.Navigator>
<Stack.Screen
name="Home"
component={() => {
return <SomeComponent />;
}}
/>
</Stack.Navigator>
);
}

component prop 期望一个 React Component,但在示例中,它获得的是一个返回 React Element 的函数。虽然表面上组件和返回 React Element 的函数看起来完全相同,但当使用时它们的行为并不相同。

在这里,每次组件重新渲染时,都会创建一个新函数并传递给 component prop。React 将看到一个新组件,并在渲染新组件之前卸载之前的组件。这将导致旧组件中的任何本地状态丢失。React Navigation 将检测并警告此特定情况,但可能还有其他方法您可能会在渲染期间创建组件,而它无法检测到。

另一个容易识别的示例是在另一个组件中创建组件时

function App() {
const Home = () => {
return <SomeComponent />;
};

const RootStack = createNativeStackNavigator({
screens: {
Home: Home,
},
});

const Navigation = createStaticNavigation(RootStack);

return <Navigation />;
}

或者当您在另一个组件中使用高阶组件(例如来自 Redux 的 connect 或接受组件的 withX 函数)时

function App() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={withSomeData(Home)} />
</Stack.Navigator>
);
}

如果您不确定,最好始终确保您用作屏幕的组件在 React 组件外部定义。它们可以在另一个文件中定义并导入,或者在同一文件的顶层作用域中定义

const Home = () => {
// ...

return <SomeComponent />;
};

const RootStack = createNativeStackNavigator({
screens: {
Home: Home,
},
});

const Navigation = createStaticNavigation(RootStack);

function App() {
return <Navigation />;
}

这不是 React Navigation 特有的,而是与一般的 React 相关的。您应该始终避免在渲染期间创建组件,无论您是否使用 React Navigation。

连接到 Chrome 调试器时,应用程序无法正常工作

当应用程序连接到 Chrome 调试器(或其他使用 Chrome 调试器的工具,例如 React Native Debugger)时,您可能会遇到各种与计时相关的问题。

这可能会导致诸如按钮按下需要很长时间才能注册或根本不起作用、手势和动画缓慢且有错误 等问题。可能还存在其他功能性问题,例如 promise 未解决、超时和间隔无法正常工作 等。

这些问题与 React Navigation 无关,而是由于 Chrome 调试器的工作原理造成的。连接到 Chrome 调试器后,您的整个应用程序都在 Chrome 上运行,并通过网络上的套接字与本机应用程序通信,这可能会引入延迟和与计时相关的问题。

因此,除非您尝试调试某些内容,否则最好在不连接到 Chrome 调试器的情况下测试应用程序。如果您使用的是 iOS,您可以选择使用 Safari 调试您的应用程序,它直接在设备上调试应用程序,并且没有这些问题,尽管它有其他缺点。