生命周期是一个抽象的概念,能让开发者产生联想记忆的往往是那些函数,比如 componentDidMount、componentWilMount 等等。然而这些函数并不是它的生命周期,只是在生命周期中按顺序被调用的函数。挂载 -> 更新 -> 销毁 。React 组件完整的流程,才是生命周期得执行流程。
挂载阶段
挂载阶段是指组件从初始化到完成加载的过程。
constructor 是类通用的构造函数,常用于初始化。所以在过去,constructor 通常用于初始化 state 与绑定函数。常见的写法如下:会强调过去呢,因为当类属性开始流行之后,React 社区的写法发生了变化,即去除了 constructor。
import React from 'react' class Counter extends React.Component { constructor(props) { super(props) this.state = { count: 0, } this.handleClick = this.handleClick.bind(this) } handleClick() { // do some stuff } render() { return null } }
getDerivedStateFromProps
本函数的作用是使组件在 props 变化时更新 state,那它什么时候才会起效呢?它的触发时机是:
- 当 props 被传入时;
- state 发生变化时;
- forceUpdate 被调用时。
我们得一个错误误区是认为只有 props 发生变化时,getDerivedStateFromProps 才会被调用,而实际上只要父级组件重新渲染时,getDerivedStateFromProps 就会被调用。所以是外部参数,也就是 props 传入时就会发生变化。
componentWillMount
一个常见的错误是 componentWillMount 跟服务器端同构渲染的时候,如果在该函数里面发起网络请求,拉取数据,那么会在服务器端与客户端分别执行一次。所以更推荐在componentDidMount中完成数据拉取操作。
render
render 函数返回的 JSX 结构,用于描述具体的渲染内容。但切记,render 函数并没有真正的去渲染组件,渲染是依靠 React 操作 JSX 描述结构来完成的。还有一点需要注意,render 函数应该是一个纯函数,不应该在里面产生副作用,比如调用 setState 或者绑定事件。
那为什么不能 setState 呢?因为 render 函数在每次渲染时都会被调用,而 setState 会触发渲染,就会造成死循环。
componentDidMount
componentDidMount 主要用于组件加载完成时做某些操作,比如发起网络请求或者绑定事件,该函数是接着 render 之后调用的。
shouldComponentUpdate
该方法通过返回 true 或者 false 来确定是否需要触发新的渲染。因为渲染触发最后一道关卡,所以也是性能优化的必争之地。通过添加判断条件来阻止不必要的渲染
componentDidUpdate
getSnapshotBeforeUpdate 的返回值会作为componentDidUpdate的第三个参数使用。
componentDidUpdate****中可以使用 setState,会触发重渲染,但一定要小心使用,避免死循环。
componentWillUnmount
该函数主要用于执行清理工作。一个比较常见的 Bug 就是忘记在 componentWillUnmount 中取消定时器,导致定时操作依然在组件销毁后不停地执行。所以一定要在该阶段解除事件绑定,取消定时器。
避免生命周期中的坑需要做好两件事:
不在恰当的时候调用了不该调用的代码;
在需要调用时,不要忘了调用。
那么主要有这么 7 种情况容易造成生命周期的坑。
- getDerivedStateFromProps 容易编写反模式代码,使受控组件与非受控组件区分模糊。
- componentWillMount 在 React中已被标记弃用,不推荐使用,主要原因是新的异步渲染架构会导致它被多次调用。 所以网络请求及事件绑定代码应移至 componentDidMount 中。
- componentWillReceiveProps 同样被标记弃用,被 getDerivedStateFromProps 所取代,主要原因是性能问题。
- shouldComponentUpdate 通过返回true 或者 false 来确定是否需要触发新的渲染。主要用于性能优化。 componentWillUpdate
- 同样是由于新的异步渲染机制,而被标记废弃,不推荐使用,原先的逻辑可结合getSnapshotBeforeUpdate 与componentDidUpdate 改造使用。
- 如果在 componentWillUnmount函数中忘记解除事件绑定,取消定时器等清理操作,容易引发 bug
- 。如果没有添加错误边界处理,当渲染发生异常时,用户将会看到一个无法操作的白屏,所以一定要添加。