文章目录
本文篇幅较长,如果你想要:
- 完整了解整个组件生命周期的变更和对应函数API,那么请从头开始;
- 快速理解传统生命周期函数API的含义和使用,那么点击目录第二部分;
- 快速了解如何在函数组件中使用useEffect来替代原有的生命周期API,那么点击目录第三部分。
一、 引言
如果你是一名前端开发者,并且使用React开发框架,那么你一定绕不开React的一个核心概念——组件生命周期。 组件的生命周期描述的是一个组件从创建,渲染,加载,显示到卸载的整个过程。其大致过程如下:
图片来源请戳此链接:http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
我们从上图可以得知,一个组件主要包括三种行为,分别是:创建,更新,卸载。每个行为又可以划分为:Render
渲染阶段和 Commit
阶段 (更新时存在Pre-Commit
阶段)。这里先简单了解一下生命周期的概况,后文会进行详细描述。
React
官方开发者在生命周期的每个阶段都提供了相关的API,我们可以使用这些API定义组件在某个生命周期进行执行相关的行为。比较常用的就是使用componentDidMount
API,在该函数内部获取来自服务器的数据。
学习这些API函数的使用并了解组件的生命周期的运行机制,是一个React开发者的基本功!
但是,我们必须不能只注重 API 的使用,而忽略了对生命周期的过程理解,API 只是 API,随时都有可能被替代!
这不,在 React v16.8 版本中,横空出世的 Hooks
(俗称钩子)颠覆了之前的类组件范式,让函数组件逐渐成为主流,越来越多的团队开始考虑基于Hooks
进行项目的重构,过去那种在类组件中调用各种生命周期API函数的方法将会慢慢成为过去(不过现在还是得学呀…)
本文不打算进行详述Hooks
的用法及优越性,已经有许多优秀的前端团队对其展开了极富深度的论述啦,我只是一个刚入门不久的前端菜鸟,暂时难以从高层的视角来评判,如果你感兴趣,本文在第四部分推荐阅读给出一些高质量的链接,你可以学到更多。
回到主题。Hooks
提供了一套新的阐释组件生命周期的方式 ,原先许多生命周期的 API 都有了对应的 Hooks
解决方案——使用强大的useEffect
这一Hooks
函数。
综上,本文主要关注以下内容:
- 类组件生命周期理解和API用法;
- 如何在函数组件中使用
useEffect
来替代类组件的生命周期API。
攥写本文的初衷是,本人最近在经常使用React
框架开发,对生命周期这一核心概念有必要进行一番梳理,同时也发现网上关于讲解如何使用新特性Hooks
来定制生命周期行为的博文数目较少而且不全面,所以我花费几个小时的时间来整理这些概念,总结方法并提供具体的代码实例,希望能帮助到大家,互相学习。
二、 组件生命周期过程及API详解
这里先放我从谷歌搜到的一张大家都在用的生命周期图(图源见链接),直接从API入手描述整个组件的生命周期过程,如果你已经使用过相关的API,那相当明了,但考虑到初学者,我还是基于个人理解给大家归纳总结一下,帮助大家理解、记忆和使用。
本文将API归为三类:
Mount
:挂载APIUpdate
: 更新APIUnmount
:卸载API
结合上述流程图,我们从这三类API开始展开叙述,简单讲解一些不常用的API (不完全覆盖),多把文字放在常用的API(基本覆盖)上。
2.1 Mount
挂载一个组件的过程是先实例化创建该组件并渲染,然后读取DOM使得组件运行在浏览器中。整个过程涉及到的 API 函数如下:
当组件在客户端实例化首次创建时,以下方法依次被调用:
getDefaultProps
(ES5语法,过时)getInitialState
(ES5语法,过时)componentWillMount
(弃用)render
(必须使用)componentDidMount
(常用)
当组件属于服务端渲染时,以下方法依次被调用:
getDefaultProps
getInitialState
componentWillMount
render
服务端渲染没有 componentDidMount
方法时因为其执行阶段是组件挂载(插入DOM
树)之后,才会执行,发生在客户端。
但需要提醒的是,getDefaultProps
和 getInitialState
方法仅适用于非ES6
语法情况,在ES6
语法中我们使用 constructor
函数来代替 getInitialState
函数,而getDefaultProps
则可以通过手动赋值指定props
属性,但已不属于函数方法了。(来源官方文档不适用ES6),但是考虑到可能还有些人依旧会使用ES5
语法,这里也会简单介绍这两个函数。(后续若非特别指明,否则讨论范围都在ES6
语法之内)
同时在图一,我们可以得知在constructor
之后,render
之前会有一个getDerivedStateFromProps
函数,这个函数在挂载和更新阶段都有可能调用,我们放在update
部分来讲。
所以下文我将介绍上述6
个函数,包括函数的作用,方法和一些注意事项。
2.1.1 constructor
这是目前常用的一个函数,我们编写的React
类组件都是React.Component
基类的继承,组件进行实例化的时候,都会调用其构造函数;如果你不初始化 state
,不绑定方法的话,你不需要为 React
组件实现构造函数(它会调用默认构造函数)。
而且在构造函数的开头,你需要调用 super(props)
, 不然会有许多蜜汁bug
难以定义和识别;
通常构造函数的作用就是:
- 初始化内部
state
变量 - 为事件处理函数绑定实例
需要注意的时,在构造函数里不要出现setState
方法,我们仅需要为this.state
赋初始值即可。
一个常见的例子如下:
// credit to https://react.docschina.org/docs/react-component.html#constructor
constructor(props) {
super(props);
// 不要在这里调用 this.setState()
this.state = {
counter: 0 };
this.handleClick = this.handleClick.bind(this);
}
2.1.2 getDefaultProps
这种方法其实只有在使用非ES6
语法定义组件(即采用ES5
语法 React.createClass
定义组件)的时候才会出现,这是一种声明默认组件属性(props)
的方法。该函数只会调用一次。
一个简单的例子:
// credit to https://www.cnblogs.com/MuYunyun/p/6629096.html#_lab2_0_0
var MyTitle = React.createClass({
getDefaultProps : function () {
return {
title : 'Hello World' // 声明默认的属性title
};
},
render: function() {
return <h1> {
this.props.title} </h1>; // 调用props属性
}
});
ReactDOM.render(
<MyTitle />,
document.body
);
而在ES6
语法中,这个函数已经被弃用了,起同样作用的写法如下:
// credit to
// https://zh-hans.reactjs.org/docs/react-without-es6.html#setting-the-initial-state
class Greeting extends React.Component {
// ...
}
Greeting.defaultProps = {
name: 'Mary'
};
2.1.3 getInitialState
在ES5
语法中,这个函数的作用相当于初始化每个组件实例的state
变量;在ES6
语法中,我们可以通过在constructor
函数里头对this.state
赋初始值,以起到同样的效果。
getInitialState
和getDefaultProps
的区别在于,getDefauProps
对于组件类来说只会调用一次,而getInitalState
是在每个组件实例化的时候都会调用,并且只调用一次。而props和state的区别在于props
是通过父组件传递,在所有实例中共享,而state
只存在组件内部,保存组件的某些状态。
这里提供一个代码实例帮助理解:
var LikeButton = React.createClass({
getInitialState: function() {
return {
liked: false};
},
handleClick: function(event) {
this.setState({
liked: !this.state.liked});
},
render: function() {
var text = this.state.liked ? 'like' : 'haven\'t liked';
return (
<p onClick={
this.handleClick}>
You {
text} this. Click to toggle.
</p>
);
}
});