React 组件生命周期函数里 setState 调用分析

注:本文是阅读《深入React技术栈》的一些心得,一部分内容和图片是直接摘抄自此书。

React 组件的生命周期函数是 react 的基础知识,如果有不熟悉的可以看官方文档。简单总结如下。
组件的生命周期函数分为两类:挂载或卸载过程 / 组件更新时。挂载或卸载过程涉及到三个函数:componentWillMount - 组件即将挂载、componentDidMount - 组件挂载完成、componentWillUnmount - 组件即将卸载。这些函数都只会在组件初始化或者卸载时运行一次。
组件更新时涉及到四个函数: shouldComponentUpdate - 判断组件是否需要更新、componentWillUpdate - 组件即将更新、componentDidUpdate - 组件完成更新、componentWillReceiveProps - 组件即将接收新的 props。


生命周期整体流程图 - 图片来自于《深入React技术栈》

setState 更是基础的不能再基础的知识了,用来更新组件的状态。需要注意的是 setState 是一个异步方法,一个生命周期内所有的 setState 方法会合并操作。

但是如果把组件的生命周期和 setState 放在一起看就会有一连串的问题:生命周期函数里可以 setState 吗?什么时候 setState 合适呢?阅读完《深入React技术栈》第一和第三章,得出以下心得:

  • 在 componentWillMount 中执行 setState 是**无意义的**,应该将这里的 setState 放到初始化 this.state 的地方去(如 constructor)直接作为 state 的初始值。原因是组件只挂载一次,在 componentWillMount 里 setState 会但是仅会更新 state 一次,而且会和 constructor 里的初始化 state 合并执行,因此这是无意义的 setState。
  • 在 componentDidMount 中执行 setState 会导致组件在初始化的时候就触发了更新,渲染了两遍,应该尽量避免。有一些场景,比如在组件 DOM 渲染完成后获得DOM元素位置或者宽高等等设置为 state,会不得在 componentDidMount 之后 setState,但是除了这些必要的时候,都应该尽量避免在 componentDidMount 里 setState。
  • 在 componentWillUnmount 中执行 setState 不会更新 state,是**不生效而且无意义**的。
  • **禁止**在 shouldComponentUpdate 和 componentWillUpdate 中调用 setState,这会造成循环调用,直至耗光浏览器内存后崩溃。了解了生命周期之后,这条很好理解。在 shouldComponentUpdate 或者 componentWillUpdate 里调用 setState 会再次触发这两个函数,然后在两个函数又触发了 setState,然后再次触发这两个函数…… 这样就进入了一个不停 setState 然后不停触发组件更新的死循环里,会导致浏览器内存耗光然后崩溃。[具体分析可以看书的第3章]
  • 在 componentDidUpdate 中执行 setState 同样会导致组件刚刚完成更新又要再更新,进入死循环。但是在某些特殊情况下,比如说 state 或者 props 变化触发了 DOM 变化,需要重新获取 DOM 元素宽高时然后更新某个
    state 的时候,就不得不在这个函数里使用 setState 了。此时一定要给 setState 设置一个前提条件( if (aaa) { setState(bbb) } ),以避免出现循环渲染的问题。 因此,如非必须,**应该尽量避免**在本函数里 setState。
  • 在 componentWillReceiveProps 中**可以** setState,不会造成二次渲染。由于只有 props 的变化才会触发 componentWillReceiveProps 事件,因为在这个事件里 setState 不会造成不停触发组件更新的死循环,可以放心在这个函数里 setState。

生命周期函数里 setState 的时机可以总结为以下表格