生命周期 - 图2
生命周期 - 图3

  • Scheduler(调度器)—— 调度任务的优先级,高优任务优先进入Reconciler
  • Reconciler(协调器)—— 负责找出变化的组件
  • Renderer(渲染器)—— 负责将变化的组件渲染到页面上

<React16

image.png

挂载阶段

单一组件挂载

  1. constructor
  2. componentWillMount
  3. render
  4. componentDidMount

image.png
有嵌套情况
image.png

更新阶段

state更新

  1. * 1. shouldComponentUpdate
  2. //返回Boolean 类型的值,判断是否需要更新渲染组件,优化 react 应用的主要手段之一
  3. // 当返回 false 就不会再向下执行生命周期了
  4. // 在这个阶段不可以 setState(),会导致循环调用。
  5. * 2. componentWillUpdate
  6. //这个生命周期主要提供一个时机能够处理一些在 Dom 发生更新之前的事情,
  7. //如获得 Dom 更新前某些元素的坐标、大小等,在这个阶段不可以 setState(),会导致循环调用。
  8. ************截止至此, this.props this.state 都还未发生更新******
  9. * 3. render
  10. * 4. componentDidUpdate
  11. //在此时已经完成渲染,Dom 已经发生变化,State 已经发生更新,
  12. //prevProps、prevState 均为上一个状态的值。

image.png

props更新

  1. * 1. componentWillReceiveProps(nextProps,nextState)
  2. //生命周期主要为我们提供对 props 发生改变的监听,
  3. //如果你需要在 props 发生改变后,相应改变组件的一些 state。
  4. // 在这个方法中改变 state 不会二次渲染,而是直接合并 state。
  5. * 2. shouldComponentUpdate(nextProps, nextState)
  6. //返回Boolean 类型的值,判断是否需要更新渲染组件,优化 react 应用的主要手段之一
  7. // 当返回 false 就不会再向下执行生命周期了
  8. // 在这个阶段不可以 setState(),会导致循环调用。
  9. * 3. componentWillUpdate
  10. //这个生命周期主要提供一个时机能够处理一些在 Dom 发生更新之前的事情,
  11. //如获得 Dom 更新前某些元素的坐标、大小等,在这个阶段不可以 setState(),会导致循环调用。
  12. ************截止至此, this.props this.state 都还未发生更新******
  13. * 4. render
  14. * 5. componentDidUpdate(prevProps, prevState)
  15. //在此时已经完成渲染,Dom 已经发生变化,State 已经发生更新,
  16. //prevProps、prevState 均为上一个状态的值。

image.png卸载阶段

  1. * componentWillUnmount
  2. //此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount 中创建的订阅等。
  3. //componentWillUnmount 中不应调用 setState,因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。
  1. import React from 'react';
  2. import './style.css';
  3. class Son extends React.Component {
  4. constructor(props) {
  5. super(props);
  6. console.log('子组件子组件constructor ======>');
  7. this.state = {
  8. number: 1
  9. };
  10. }
  11. // static getDerivedStateFromProps(props, state) {
  12. // console.log('getDerivedStateFromProps =======>');
  13. // return props;
  14. // }
  15. componentWillMount() {
  16. console.log('子组件componentWillMount =====>');
  17. }
  18. componentWillReceiveProps(nextProps, nextState) {
  19. //主要提供对 props 发生改变的监听,如果需要在 props 发生改变后,相应改变组件的一些 state。
  20. //在这个方法中改变 state 不会二次渲染,而是直接合并 state。
  21. console.log(
  22. '子组件componentWillReceiveProps ===>',
  23. 'nextProps',
  24. nextProps,
  25. 'nextState',
  26. nextState
  27. );
  28. }
  29. componentDidMount() {
  30. console.log('子组件componentDidMount ======>');
  31. }
  32. shouldComponentUpdate(nextProps, nextState) {
  33. //返回Boolean 类型的值,判断是否需要更新渲染组件,优化 react 应用的主要手段之一
  34. // 当返回 false 就不会再向下执行生命周期了
  35. // 在这个阶段不可以 setState(),会导致循环调用。
  36. console.log(
  37. '子组件shouldComponentUpdate ====>',
  38. 'nextProps',
  39. nextProps,
  40. 'nextState',
  41. nextState,
  42. 'this.state',
  43. this.state
  44. );
  45. return true;
  46. }
  47. componentWillUpdate() {
  48. console.log('子组件componentWillUpdate =====>', 'this.state', this.state);
  49. }
  50. componentDidUpdate(prevProps, prevState) {
  51. console.log(
  52. '子组件componentDidUpdate =====>',
  53. 'prevProps',
  54. prevProps,
  55. 'prevState',
  56. prevState,
  57. 'this.state',
  58. this.state
  59. );
  60. }
  61. componentWillUnmount() {
  62. console.log('子组件componentWillUnmount=====>');
  63. }
  64. render() {
  65. console.log('子组件render ========>');
  66. return (
  67. <>
  68. <h1>父组件props{this.props.count}</h1>
  69. <button onClick={() => this.setState({ count: this.state.number + 1 })}>
  70. 点击子组件更新状态{this.state.number}
  71. </button>
  72. </>
  73. );
  74. }
  75. }
  76. class Parent extends React.Component {
  77. constructor(props) {
  78. super(props);
  79. console.log('constructor ======>');
  80. this.state = {
  81. count: 1
  82. };
  83. }
  84. // static getDerivedStateFromProps(props, state) {
  85. // console.log('getDerivedStateFromProps =======>');
  86. // return props;
  87. // }
  88. componentWillMount() {
  89. console.log('componentWillMount =====>');
  90. }
  91. componentWillReceiveProps(nextProps, nextState) {
  92. //主要提供对 props 发生改变的监听,如果需要在 props 发生改变后,相应改变组件的一些 state。
  93. //在这个方法中改变 state 不会二次渲染,而是直接合并 state。
  94. console.log('componentWillReceiveProps ===>', nextProps, nextState);
  95. }
  96. componentDidMount() {
  97. console.log('componentDidMount ======>');
  98. }
  99. shouldComponentUpdate(nextProps, nextState) {
  100. //返回Boolean 类型的值,判断是否需要更新渲染组件,优化 react 应用的主要手段之一
  101. // 当返回 false 就不会再向下执行生命周期了
  102. // 在这个阶段不可以 setState(),会导致循环调用。
  103. console.log(
  104. 'shouldComponentUpdate ====>',
  105. // 'nextProps',
  106. // nextProps,
  107. // 'nextState',
  108. // nextState,
  109. 'this.state',
  110. this.state
  111. );
  112. return true;
  113. }
  114. componentWillUpdate() {
  115. console.log('componentWillUpdate =====>', 'this.state', this.state.count);
  116. }
  117. componentDidUpdate(prevProps, prevState) {
  118. console.log(
  119. 'componentDidUpdate =====>',
  120. 'prevProps',
  121. prevProps,
  122. 'prevState',
  123. prevState,
  124. 'this.state',
  125. this.state
  126. );
  127. }
  128. componentWillUnmount() {
  129. console.log('componentWillUnmount=====>');
  130. }
  131. render() {
  132. console.log('render ========>', this.state.count);
  133. return (
  134. <>
  135. <h1>生命周期测试</h1>
  136. <button onClick={() => this.setState({ count: this.state.count + 1 })}>
  137. 点击更新状态{this.state.count}
  138. </button>
  139. <Son count={this.state.count} />
  140. </>
  141. );
  142. }
  143. }
  144. export default function App() {
  145. return (
  146. <div>
  147. <h1>Hello StackBlitz!</h1>
  148. <p>Start editing to see some magic happen :)</p>
  149. <Parent />
  150. </div>
  151. );
  152. }

React16+

https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
image.png

组件在挂载dom上的时候,会经历 render和commit阶段

Stack Reconciler重构为Fiber Reconciler后,render阶段的任务可能中断/重新开始,对应的组件在render阶段的生命周期钩子(即componentWillXXX)可能触发多次。这种行为和Reactv15不一致,所以标记为UNSAFE。更详细的解释参照这里(opens new window)为此,React提供了替代的生命周期钩子getSnapshotBeforeUpdate。`
Stack Reconciler重构为Fiber Reconciler后,render阶段的任务可能中断/重新开始,对应的组件在render阶段的生命周期钩子(即componentWillXXX)可能触发多次。这种行为和Reactv15不一致,所以标记为UNSAFE
。更详细的解释参照这里(opens new window)为此,React提供了替代的生命周期钩子getSnapshotBeforeUpdate。`

render阶段

没有副作用,可能被中断
包含 constructor 、getDerivedStateFromProps 、shouldComponentUpdate、render
componentWillRecieveProps/componentWillUpdate/componentWillMount被废弃,由getDerivedStateFromProps代替

commit阶段

有副作用,可以操作dom
包含componentDidMount、componentDidUpdate、componentWilUnmount

React 16 中删除了如下三个生命周期。

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

组件挂载流程

调用ReactDom.render
进入render阶段
采用深度优先遍历算法创建fiber树
进入commit阶段

初始进入页面

  1. constructor
  2. getDerivedStateFromProps
  3. render
  4. componentDidMount

image.png

状态更新

  1. getSnapshotBeforeUpdate(prevProps, prevState) =>
  2. shouldComponentUpdate
  3. render
  4. getSnapshotBeforeUpdate =>
  5. // 必须有返回值 返回值作为 componentDidUpdate的第三个参数可以获取
  6. componentDidUpdate(prevProps, prevState, valueFromSnapShot) =>

image.png
有嵌套情况
image.png

  1. import React from 'react';
  2. import './style.css';
  3. class Son extends React.Component {
  4. constructor(props) {
  5. super(props);
  6. console.log('子组件子组件constructor ======>');
  7. this.state = {
  8. number: 1
  9. };
  10. this.sonRef = React.createRef(null);
  11. }
  12. static getDerivedStateFromProps(props, state) {
  13. console.log('子组件getDerivedStateFromProps =======>', props, state);
  14. return props;
  15. }
  16. componentDidMount() {
  17. console.log('子组件componentDidMount ======>');
  18. }
  19. shouldComponentUpdate(nextProps, nextState) {
  20. console.log(
  21. '子组件shouldComponentUpdate ====>',
  22. 'nextProps',
  23. nextProps,
  24. 'nextState',
  25. nextState,
  26. 'this.state',
  27. this.state
  28. );
  29. return true;
  30. }
  31. getSnapshotBeforeUpdate(prevProps, prevState) {
  32. // ...
  33. console.log('子组件getSnapshotBeforeUpdate===>', prevProps, prevState);
  34. return this.sonRef;
  35. }
  36. componentDidUpdate(prevProps, prevState, valueFromSnapShot) {
  37. console.log(
  38. '子组件componentDidUpdate =====>',
  39. 'prevProps',
  40. prevProps,
  41. 'prevState',
  42. prevState,
  43. 'valueFromSnapShot',
  44. valueFromSnapShot,
  45. 'this.state',
  46. this.state
  47. );
  48. }
  49. componentWillUnmount() {
  50. console.log('子组件componentWillUnmount=====>');
  51. }
  52. render() {
  53. console.log('子组件render ========>');
  54. return (
  55. <div ref={this.sonRef}>
  56. <h1>父组件props{this.props.count}</h1>
  57. <button onClick={() => this.setState({ count: this.state.number + 1 })}>
  58. 点击子组件更新状态{this.state.number}
  59. </button>
  60. </div>
  61. );
  62. }
  63. }
  64. class Parent extends React.Component {
  65. constructor(props) {
  66. super(props);
  67. console.log('constructor ======>');
  68. this.state = {
  69. count: 1
  70. };
  71. }
  72. static getDerivedStateFromProps(props, state) {
  73. console.log('getDerivedStateFromProps =======>', props, state);
  74. return props;
  75. }
  76. componentDidMount() {
  77. console.log('componentDidMount ======>');
  78. }
  79. shouldComponentUpdate(nextProps, nextState) {
  80. console.log(
  81. 'shouldComponentUpdate ====>',
  82. // 'nextProps',
  83. // nextProps,
  84. // 'nextState',
  85. // nextState,
  86. 'this.state',
  87. this.state
  88. );
  89. return true;
  90. }
  91. getSnapshotBeforeUpdate(prevProps, prevState) {
  92. // ...
  93. console.log('getSnapshotBeforeUpdate===>', prevProps, prevState);
  94. }
  95. componentDidUpdate(prevProps, prevState) {
  96. console.log(
  97. 'componentDidUpdate =====>',
  98. 'prevProps',
  99. prevProps,
  100. 'prevState',
  101. prevState,
  102. 'this.state',
  103. this.state
  104. );
  105. }
  106. componentWillUnmount() {
  107. console.log('componentWillUnmount=====>');
  108. }
  109. render() {
  110. console.log('render ========>', this.state);
  111. return (
  112. <>
  113. <h1>生命周期测试</h1>
  114. <button onClick={() => this.setState({ count: this.state.count + 1 })}>
  115. 点击更新状态{this.state.count}
  116. </button>
  117. <Son count={this.state.count} />
  118. </>
  119. );
  120. }
  121. }
  122. export default function App() {
  123. return (
  124. <div>
  125. <h1>Hello StackBlitz!</h1>
  126. <p>Start editing to see some magic happen :)</p>
  127. <Parent />
  128. </div>
  129. );
  130. }

getDerivedStateFromProps

getDerivedStateFromProps exists for only one purpose. It enables a component to update its internal state as the result of changes in props.

  1. static getDerivedStateFromProps(nextprops, prestate)
  • 在 React 16.3.0 版本中:在组件实例化、接收到新的 props 时会被调用
  • 在 React 16.4.0 版本中:在组件实例化、接收到新的 props 、组件状态更新时会被调用

注意

  • 静态方法。不属于任何实例,内部的this并不指向组件本身,this. 的任何属性和方法都不能用。

为什么getDerivedStatefromProps是静态的?
隔离组件实例访问,不能用this.setState()等一些方法,保持该方法的纯粹,它就是用来定义派生state的,除此之外不能进行任何操作
保持它是纯函数,不要产生副作用。

  • 返回一个对象来更新 state,如果返回 null 则不更新任何内容
  • 由上图可以知道,props更新,setState,forceUpdate会触发getDerivedStateFromProps
  1. forceUpdate(callback)

默认情况下,当组件的 state 或 props 发生变化时,组件将重新渲染。如果 render() 方法依赖于其他数据,则可以调用 forceUpdate() 强制让组件重新渲染。

调用 **forceUpdate()** 将致使组件调用 **render()** 方法,此操作会跳过该组件的 **shouldComponentUpdate()**。但其子组件会触发正常的生命周期方法,包括 **shouldComponentUpdate()** 方法。

getSnapshotBeforeUpdate

  1. getSnapshotBeforeUpdate(prevProps, prevState)

替换之前的willX的生命周期,强制用户在mount阶段获取dom

getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()

References

https://mp.weixin.qq.com/s/G0xcaQ1KQ9cBgO3yybhDpA
https://github.com/sisterAn/blog/issues/34