深入浅出 React 18 中的严格模式
React 已经出现很长时间了。每个主要版本都向我们介绍了处理 UI 问题的新技术、工具和方法。
React 在 2022 年 3 月发布了 v18 版本,其中包括了一些架构上的更改。这个版本主要关注并发模式、新的 React hook 以及 React 的 Strict Mode API 的更改。虽然严格模式作为 React 的一个特性已经有很长一段时间了,但 v18 使它在捕获早期 bug 方面更有效,从而使代码库更可预测。
在本文中,你将了解严格模式以及引入它的初衷。你将了解它的各种特性,以及 v18 版本如何改进其 API 并提供与 hook 更好的兼容性。
1. React 的严格模式介绍
严格模式可以被认为是 "use strict"
表示。这是一段时间以前在 ECMAScript v5 中引入的,确保了 JavaScript 的更严格版本。
"use strict";
x = 3.1415;
上面的例子会抛出一个错误,因为 x
没有定义。注意在文件顶部添加 "use strict"
是如何确保这一点的。在没有添加 "use strict"
的情况下,你甚至可能不会得到这个错误,因为如果没有严格类型定义(如 "use strict"
, TypeScript 等),JavaScript 往往会执行奇怪的行为。
类似地,React 中的严格模式是一个只针对开发的工具,它在编写 React 代码时强制执行更严格的警告和检查。
你可以为任何组件启用 StrictMode
,简单地将组件名称作为 children prop 包装在 StrictMode
中,像这样:
<StrictMode>
<Button />
</StrictMode>
<StrictMode>
<Navbar />
</StrictMode>
更推荐的做法是用 StrictMode
包装 App
根组件。注意,App
通常是 create-react-app 和 Next.js 中的根组件。
<StrictMode>
<App />
</StrictMode/>
这在整个 React 代码库中强制在开发时间执行检查和警告。当然,确保像这样导入 StrictMode
:
import { StrictMode } from 'react'
<StrictMode>
......
</StrictMode>
或者像这样:
代码语言:javascript复制import React from 'react'
<React.StrictMode>
.....
</React.StrictMode>
现在,我们将更深入地研究严格模式的各种含义,并帮助在开发早期发现问题。
2. 使用不安全的生命周期方法的警告
React 基于类的生命周期方法经历了一系列 API 更改。为了支持更现代的 API,许多曾经被广泛使用的方法现在都被正式弃用了。
React 的严格模式现在会警告开发人员,如果他们正在使用这些被弃用的 API,如 componentWillMount
、componentWillReceiveProps
和 componentWillUpdate
。这些 API 现在被认为是不安全的,所以 React 在这些 API 名称前添加了一个不安全的前缀:
UNSAFE_componentWillMount
UNSAFE_componentWillReceiveProps
UNSAFE_componentWillUpdate
严格模式甚至足够聪明,可以在使用的任何第三方包中包含这些已弃用的 API 时警告开发人员。你可以自己修改这些包,也可以选择一个替代方案。
3. 推荐使用 createRef API 而不是传统字符串 ref
如果你使用 React 时,基于类的体系结构实际上是创建组件的方式,你可能会使用字符串 ref
API:
class Form extends Component {
render() {
return <input onClick={() => this.focus()} ref='input' />;
}
focus() {
console.log(this.refs.input.value);
}
}
虽然易于阅读和使用,但由于以下几个原因,这个 API 现在被认为是遗留的问题,包括:
- 包装的组件不能确定它的子组件是否已经有一个引用。这个问题可以通过使用回调引用模式来解决
- 字符串引用 API 很难读,也很难用类型检查器进行静态分析
React 的严格模式警告开发者要么使用回调模式,要么使用更现代的 createRef
API。
4. 使用已弃用的 findDOMNode 的警告
findDOMNode
是一个基于类的 API,用于从任何组件定位 DOM 树的深层元素。这个 API 这看起来很好,但实际上会导致 React 的抽象原则出现问题。
父元素必须确保其子元素向下延伸并呈现正确的 DOM 节点。一个很大的缺点是,findDOMNode
只是一个一次性调用 API,因此如果任何节点元素由于某些状态更新而更改,它将不会用 findDOMNode
API 反映和更新。
考虑到所有这些缺点,严格模式警告你不要使用这个 API,它可能会在未来的 React 版本中被删除。大多数情况下,现在可以使用 ref
来瞄准 DOM 元素。你可以简单地将 ref
引用附加到需要瞄准的元素。
class Form extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
// 处理 textInput。当前逻辑在这里
render() {
return (
<input
type="text"
ref={this.textInput}
/>
);
}
}
5. 检测意外副作用
React 的严格模式针对流行的内置 hook(如 useState
、useMemo
和 useReducer
)做了一些有趣的事情。具体来说,它在开发模式中调用这些函数两次,在生产模式中调用一次(如预期的那样)。
这可能会在调试代码时造成一些混乱,但是通过这样做,严格模式确保检查潜在的内存泄漏。它还有助于使严格模式下的代码更具确定性。
不仅限于函数式组件,在基于类的体系结构中也可以发现调用函数两次的相同行为,例如在 constructor
,render
, shouldComponentUpdate
等中。
如果你使用的是 create-react-app,那么整个应用程序都会默认使用严格模式。在类组件中使用这些 hook 或状态更新器函数时,甚至会看到控制台消息被记录两次。
在 v18 之前,当函数被调用两次时,React 会立即关闭第二个 console.log
方法。但是,在 v18 中,React 不会隐瞒任何日志,从而为开发人员提供更多的透明度。所有这些日志现在都在任何函数、hook 等的双重调用期间被调用两次。
6. 遗留的 context API 的警告
与 ref API 类似,我们也有一个 context API。严格模式警告不要使用遗留 context API,因为它将从未来的版本中删除。相反,我们有一个更现代的 context API,它使用 「提供者—使用者模式」。
代码语言:javascript复制const ThemeContext = React.createContext('dark')
// 在这里使用它
<ThemeContext.Provider value={data}>
{children}
</ThemeContext.Provider>
这是现在使用新的 context
API 处理应用程序状态 context
的推荐方法。
7. React v18 卸载和重新挂载体系结构
React v18 引入了关于卸载和重新挂载的新的严格模式行为。现在,每个元素都将被卸载和重新挂载,其状态和效果与元素第一次挂载时相同。
典型的卸载和重新挂载周期如下所示:
- 元素第一次被挂载
- 产生了副作用
- 严格模式现在模仿副作用的破坏
- 副作用将应用于挂载的组件
这使得 React 代码更具弹性,并有助于保存 UI 的状态。例如,如果用户在第一个选项卡上,并立即在第一个和第二个选项卡之间来回切换,React 需要确保正确的元素块被挂载和销毁,同时保持正确的 UI 状态和副作用。
从 v18 开始,严格模式具有这种额外的仅用于开发的行为。
8. 小结
你现在已经介绍了 React v18 严格模式更新中的所有内容!我们已经看到了严格模式如何影响开发模式工具。它有自己的一组规则和行为,确保对代码库进行严格的警告和检查。这不仅有助于开发人员使代码库为未来做好准备,而且还有助于重构。
官方 React 团队建议执行应用范围内的严格模式,以最大限度地利用它。在未来的 React 版本中,我们希望严格模式能提供更多的特性,帮助像我们这样的开发人员获得更好的工具支持。