React已经存在了相当长的一段时间了。每个主要的版本都向我们介绍了新的技术、工具和处理UI问题的方法。
React在2022年3月发布了v18,其中包括一些架构上的变化。这个版本主要集中在并发模式、新的React钩子和React的严格模式API的行为变化上。虽然严格模式已经是React的一项功能,但v18使其在捕捉早期bug方面更加有效,从而使代码库更加可预测。
在这篇文章中,你将了解严格模式以及它最初被引入的原因。你会看到它的各种特性,以及v18版本是如何改进其API并提供更好的钩子兼容性的。
严格模式正试图与React的基于悬念的架构一起为未来做好准备,使其在内省UI问题时更具弹性。让我们开始吧!
React的严格模式介绍
严格模式可以被认为是 ["use strict"](https://www.w3schools.com/js/js_strict.asp)符号。这是一段时间前在ECMAScript v5中引入的,确保了JavaScript的更严格的版本:
"use strict";
x = 3.1415;
上面的例子会抛出一个错误,因为x 没有被定义。请注意,在文件顶部添加"use strict" 是如何确保这一点的。在没有添加"use strict" 的情况下,你甚至可能不会得到这个错误,因为如果不受严格类型定义的约束,JavaScript往往会执行奇怪的行为(如"use strict" 、TypeScript、flow等)。
同样,React中的严格模式是一个开发专用的工具,在你编写React代码时执行更严格的警告和检查。
你可以为任何组件启用StrictMode ,只需在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>
或像这样:
import React from 'react'
<React.StrictMode>
.....
</React.StrictMode>
现在,我们将深入研究严格模式的各种影响,它可以在开发过程中更早地帮助捕捉问题。
关于使用不安全生命周期方法的警告
React的基于类的生命周期方法经历了一系列的API变化。很多曾经被广泛使用的方法现在已被正式废弃,并被不鼓励使用,以支持更现代的API。
React的严格模式现在会警告开发者,如果他们使用这些被废弃的API,如componentWillMount 、componentWillReceiveProps 、和componentWillUpdate 。这些现在被认为是不安全的使用,以至于React在这些API名称上添加了一个UNSAFE 前缀:
UNSAFE_componentWillMountUNSAFE_componentWillReceivePropsUNSAFE_componentWillUpdate
严格模式甚至足够聪明,如果正在使用的任何第三方包包含这些废弃的API,它会警告开发者。你可以自己修改这些包,或者选择一个替代品。
推荐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可读性强,使用方便,但由于一些原因,现在被认为是一种遗留问题,包括:
- 一个被包裹的组件无法弄清它的子组件是否已经有了一个ref。这个问题可以用回调ref模式来解决。
- 字符串ref API很难用类型检查器来阅读和进行静态分析。
React的严格模式警告开发者使用回调模式或更现代的createRef API来代替。
关于废弃的findDOMNode 使用的警告
[findDOMNode](https://blog.logrocket.com/managing-dom-components-reactdom/)是一个基于类的API,用于从任何组件中锁定DOM树深处的一个元素:
class Layout extends Component {
componentDidMount() {
const nodeElement = ReactDOM.findDOMNode(this);
}
render () {
return <Navigation>{this.props.children}</Navigation>;
}
}
这看起来很好,但它实际上在React的抽象原则中造成了问题。
父元素必须确保其子元素向下延伸并呈现正确的DOM节点。一个很大的缺点是,findDOMNode 仅仅是一个一次性的调用API,所以如果任何节点元素由于一些状态的更新而被向下改变,它将不会被反映和更新到findDOMNode API。
考虑到所有这些缺点,严格模式警告你不要使用这个API,它可能在未来的React版本中被删除。
大多数时候,DOM元素现在可以使用ref 作为目标。你可以简单地将一个ref 引用附加到你需要定位的元素上:
class Form extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
// handle textInput.current logic here //
render() {
return (
<input
type="text"
ref={this.textInput}
/>
);
}
}
检测意外的副作用
React的严格模式对流行的内置钩子做了一些有趣的事情,如useState 、useMemo 和useReducer 。具体来说,它在开发中调用这些函数两次,在生产模式中调用一次(如预期)。
这在调试代码时可能会造成一些混乱,但通过这样做,严格模式确保了检查潜在的内存泄漏。这也有助于使严格模式的代码更具有确定性。
不仅仅局限于功能组件,在基于类的架构中也可以发现两次调用函数的相同行为,比如在constructor 、render 、shouldComponentUpdate ,等等。
如果你使用的是create-react-app,整个应用的严格模式是默认的。当使用这些钩子或类组件中的状态更新器函数时,你会看到即使是控制台信息也会被记录两次。
在v18之前,当函数被调用两次时,React会立即让第二个console.log 方法沉默。但是,在v18中,React并没有压制任何日志,以使开发者有更多的透明度。所有这些日志现在都会在任何函数、钩子等的双重调用中被调用两次。
对遗留的上下文API的警告
与 refs API 类似,我们也有一个传统的上下文 API。严格模式警告不要使用传统的上下文 API,因为它将在未来的版本中被删除。相反,我们有一个更现代的上下文API,使用提供者-消费者模式:
const ThemeContext = React.createContext('dark')
// consume it here
<ThemeContext.Provider value={data}>
{children}
</ThemeContext.Provider>
这是现在使用新的上下文API处理应用程序状态上下文的推荐方式。
React v18的卸载和重挂架构
React v18引入了新的关于卸载和重挂的严格模式行为。现在,每个元素在被卸载和重新挂载时,其状态和效果与元素第一次被挂载时相同。
一个典型的挂载和重新挂载周期可能是这样的:
- 元素第一次被挂载
- 产生副作用
- 严格模式现在模仿了效果的破坏
- 副作用将被应用于挂载的组件
这使得React代码更有弹性,并有助于保存UI的状态。例如,如果用户在第一个标签页上,并立即在第一个和第二个标签页之间来回切换,React需要确保正确的元素块被加载和销毁,同时保留正确的UI状态和副作用。
从v18开始,严格模式有这个额外的开发专用的行为。
总结
你现在已经涵盖了React v18的严格模式更新中的所有内容!
我们已经看到严格模式是如何影响开发模式的工具的。它有自己的一套规则和行为,确保对代码库的严格警告和检查。这不仅可以帮助开发人员使代码库适应未来,也可以帮助他们进行重构。
官方的React团队建议在整个应用范围内执行严格模式,以获得它的最大效益。对于未来的React版本,预计严格模式将获得更多的功能,以帮助像我们这样的开发者获得更好的工具支持。