跳到主要内容

快速刷新

快速刷新是 React Native 的一项功能,它能让你在修改 React 组件时获得近乎即时的反馈。快速刷新默认启用,你可以在React Native 开发菜单中切换“启用快速刷新”。启用快速刷新后,大多数修改应在一两秒内可见。

工作原理

  • 如果你编辑的模块**只导出 React 组件**,快速刷新将只更新该模块的代码,并重新渲染你的组件。你可以编辑该文件中的任何内容,包括样式、渲染逻辑、事件处理程序或副作用。
  • 如果你编辑的模块导出内容*不是* React 组件,快速刷新将重新运行该模块以及导入它的其他模块。因此,如果 Button.jsModal.js 都导入了 Theme.js,那么编辑 Theme.js 将更新这两个组件。
  • 最后,如果你**编辑的文件**被**React 树之外的模块导入**,快速刷新**将回退到完全重新加载**。你可能有一个文件,它渲染一个 React 组件,但同时也导出一个被**非 React 组件**导入的值。例如,你的组件可能还导出一个常量,而一个非 React 实用模块导入了它。在这种情况下,考虑将该常量迁移到一个单独的文件中,并将其导入到这两个文件中。这将重新启用快速刷新功能。其他情况通常也可以用类似的方法解决。

错误恢复能力

如果在快速刷新会话期间出现**语法错误**,你可以修复它并再次保存文件。红框将消失。带有语法错误的模块会停止运行,因此你无需重新加载应用程序。

如果在**模块初始化期间发生运行时错误**(例如,输入 Style.create 而不是 StyleSheet.create),一旦你修复错误,快速刷新会话将继续。红框将消失,模块将更新。

如果你犯了一个导致**组件内部运行时错误**的错误,修复错误后,快速刷新会话也*将*继续。在这种情况下,React 将使用更新后的代码重新挂载你的应用程序。

如果你的应用程序中有错误边界(这对于生产环境中的优雅失败是个好主意),它们将在红框出现后的下一次编辑时重试渲染。从这个意义上说,拥有一个错误边界可以防止你总是被踢出到根应用程序屏幕。但是,请记住错误边界不应该*过于*细粒度。它们在生产环境中被 React 使用,并且应该始终经过精心设计。

局限性

快速刷新会尝试保留你正在编辑的组件中的局部 React 状态,但前提是这样做是安全的。以下是你可能会看到每次编辑文件时局部状态都被重置的一些原因:

  • 类组件的局部状态不会被保留(只有函数组件和 Hooks 会保留状态)。
  • 你正在编辑的模块除了 React 组件之外,可能还有*其他*导出。
  • 有时,一个模块会导出调用高阶组件(例如 createNavigationContainer(MyScreen))的结果。如果返回的组件是类组件,状态将被重置。

从长远来看,随着你的代码库更多地转向函数组件和 Hooks,你可以期望在更多情况下保留状态。

技巧

  • 快速刷新默认会保留函数组件(和 Hooks)中的 React 局部状态。
  • 有时你可能希望*强制*重置状态并重新挂载组件。例如,如果你正在调整一个只在挂载时发生的动画,这会很方便。为此,你可以在你正在编辑的文件中的任何位置添加 // @refresh reset。此指令仅限于该文件,并指示快速刷新在每次编辑时重新挂载在该文件中定义的组件。

快速刷新与 Hooks

在可能的情况下,快速刷新会尝试在编辑之间保留组件的状态。特别是,useStateuseRef 会保留它们之前的值,只要你不改变它们的参数或 Hook 调用的顺序。

带有依赖项的 Hooks——例如 useEffectuseMemouseCallback——在快速刷新期间*总是*会更新。在快速刷新发生时,它们的依赖项列表将被忽略。

例如,当你将 useMemo(() => x * 2, [x]) 编辑为 useMemo(() => x * 10, [x]) 时,它将重新运行,即使 x(依赖项)没有改变。如果 React 不这样做,你的修改就不会反映在屏幕上!

有时,这可能导致意想不到的结果。例如,即使是一个依赖项为空数组的 useEffect 也会在快速刷新期间重新运行一次。然而,编写能够弹性应对 useEffect 偶尔重新运行的代码是一个好习惯,即使没有快速刷新也是如此。这使得你以后更容易为其引入新的依赖项。