快速刷新
快速刷新(Fast Refresh)是 React Native 的一项功能,允许你对 React 组件的更改获得近乎即时的反馈。快速刷新默认启用,你可以在React Native 开发者菜单中切换“启用快速刷新”。启用快速刷新后,大多数编辑应该在一两秒内可见。
工作原理
- 如果你编辑了一个只导出 React 组件的模块,快速刷新将只更新该模块的代码,并重新渲染你的组件。你可以编辑该文件中的任何内容,包括样式、渲染逻辑、事件处理程序或副作用。
- 如果你编辑的模块导出的内容不是 React 组件,快速刷新将重新运行该模块以及所有导入它的其他模块。因此,如果
Button.js和Modal.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
在可能的情况下,快速刷新会尝试在编辑之间保留组件的状态。特别是,只要你不更改 useState 和 useRef 的参数或 Hook 调用的顺序,它们就会保留其先前的值。
具有依赖项的 Hooks(例如 useEffect、useMemo 和 useCallback)在快速刷新期间总是会更新。在快速刷新发生时,它们的依赖项列表将被忽略。
例如,当你将 useMemo(() => x * 2, [x]) 编辑为 useMemo(() => x * 10, [x]) 时,即使 x(依赖项)没有改变,它也会重新运行。如果 React 不这样做,你的编辑就不会反映在屏幕上!
有时,这可能导致意想不到的结果。例如,即使是具有空依赖项数组的 useEffect 在快速刷新期间仍会重新运行一次。但是,即使没有快速刷新,编写对偶尔重新运行 useEffect 具有弹性的代码也是一个好习惯。这使你以后更容易为其引入新的依赖项。