React 组件的生命周期

@一棵菜菜  May 31, 2019

说明

React 组件的生命周期笔记整理。

官方文档

生命周期函数:指在某一个时刻组件会自动调用执行的函数

React v16.0前的生命周期

react 16.0 之前的生命周期.png

红框中的是react 16.0 之后已废弃的。

我们的react 版本是:15.5.4。

React v16.3 的生命周期

react 16.3 生命周期函数.png


React v16.4 及之后的生命周期图

组件的生命周期【必会】

1. 挂载

当组件实例被创建并插入 DOM 中时

  • constructor() ——设置state、props
  • render()
  • react更新DOM
  • componentDidMount()

2. 更新

当组件的 propsstate(通过setState()) 发生变化时会触发更新

  • render()
  • react更新DOM
  • componentDidUpdate()
props的改变,其实也是因为父组件的state的改变

3. 卸载

当组件从 DOM 中移除时

  • componentWillUnmount()
注意:render()函数必须自己定义!

生命周期图(常用的)

生命周期(常用的).png

生命周期图(全)

生命周期(全).png

  • get derived state from props:从prop获取派生的状态(此方法适用于罕见的用例,即 state 的值在任何时候都取决于 props)[dɪ'raɪvd]
  • get snapshot before update: 更新前获取快照 ['snæpʃɒt]
官方生命周期图

介绍

1. constructor()

如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。

在 React 中,构造函数仅用于以下两种情况:

  1. this.state 赋值:通过给 this.state 赋值对象来初始化内部 state
  2. 为事件处理函数绑定实例

注意

  • 只能在构造函数中直接为 this.state 赋值!且不要在 constructor() 函数中调用 this.setState()
  • 更新state则应使用this.setState()
  • 要避免在构造函数中引入任何副作用或订阅。如遇到此场景,请将对应的操作放置在 componentDidMount 中。
  • 避免将 props 的值复制给 state!这是一个常见的错误!

2. render()

render() 函数应该为纯函数!
如需与浏览器进行交互,请在 componentDidMount() 或其他生命周期方法中执行你的操作。保持 render() 为纯函数,可以使组件更容易思考。

有副作用的操作,如 API 请求和路由跳转;
非纯函数,如Date.now() 或 Math.random()

纯函数:给定固定的输入,就一定会有固定的输出,而且不会有任何副作用。(一旦函数内有异步操作setTimeoutsetIntervalajax请求 等)、new Date()Math.random()、内部对函数的参数做修改 等时,就不是纯函数了)

redux中的reducer()函数也是纯函数

【注意】

  1. 当组件的stateprops发生改变时,会重新执行render()函数;
  2. 当父组件的render()函数执行时,它的子组件的render()函数也都将被重新执行一次。(不论是否有传数据给字组件。)

3. componentDidMount()

在组件挂载后(插入 DOM 树中)会立即调用componentDidMount()

  • 依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,此处是实例化请求的好地方。【重点】
  • 这个方法是比较适合添加订阅的地方(如果添加了订阅,请不要忘记在 componentWillUnmount() 里取消订阅)【不太理解???】
你可以在 componentDidMount() 里可以直接调用 setState()(注意是直接调用,而不是在异步函数如setTimeout、ajax调用哈)。它将触发额外渲染,但此渲染会发生在浏览器更新屏幕之前。如此保证了即使在 render() 两次调用的情况下,用户也不会看到中间状态。请谨慎使用该模式,因为它会导致性能问题

4. componentDidUpdate()

在更新后会立即调用componentDidUpdate()。首次渲染不会执行此方法。

componentDidUpdate(prevProps, prevState, snapshot)

5. componentWillUnmount()

在组件卸载及销毁之前会直接调用componentWillUnmount()

在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。 ,但不应再调用 setState()了(毫无意义了)。


shouldComponentUpdate(nextProps, nextState)—— 可减少重复渲染

推荐文章《react如何通过shouldComponentUpdate来减少重复渲染》

函数内必须return true 或者return false;

为什么需要使用shouldComponentUpdate?

在react开发中,经常会遇到组件重复渲染的问题:父组件一个state的变化(则父组件会重新执行一次render函数),就会导致该组件的所有子组件都重新执行各自的render,尽管绝大多数子组件的props没有变化

WechatIMG179.jpeg

React会根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 stateprops 更改的影响。
当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。返回值默认为 true,则当 props 或 state 发生变化时,会生成新的虚拟DOM,对比新老虚拟DOM,更新真实的DOM。首次渲染或使用 forceUpdate() 时不会调用该方法。

优点

可提高组件的性能,因为可以避免组件render()函数的无谓运行(会引起生成新的虚拟DOM、新老虚拟DOM的比对、真实DOM的渲染)。

但是,state里的数据这么多,还有对象,还有复杂类型数据,react的理念就是拆分拆分再拆分,这么多子组件,我要每个组件都去自己一个一个对比吗?不存在的,这么麻烦,要知道我们的终极目标是不劳而获:可以使用React.PureComponent(但这个只是做了浅比较,复杂的话还可以考虑pureComponent 配合使用插件immutable-js)。

具体查看我的文章里的这部分内容:React.PureComponent+immutable.js
我们公司的组件大部分都是使用React.pureComponent的哟,也使用了immutable

React.ComponentReact.PureComponent的区别?

都可以被继承来创建react组件。

  • React.PureComponent:内部以浅层对比 propstate 的方式实现了shouldComponentUpdate()函数。(注意:因为是浅比较,所以仅适合在你的 props 和 state 较为简单时使用。适合纯展示组件。(PureComponent含义为纯洁的组件——我的理解)
  • React.Component:未实现,所以需要我们自己调用shouldComponentUpdate()生命周期函数进行操作。
推荐文章《React之PureComponent》
官方文档
浅比较:会比较 Object.keys(state | props) 的长度是否一致,每一个 key 是否两者都有,并且是否是一个引用,也就是只比较了第一层的值,确实很浅,所以深层的嵌套数据是对比不出来的。

比如:

this.state = {a:1,b:{name:'caicai'}};
var copy = Object.assign({},this.state);// 浅复制
copy.b.name = 'shitou';
copy.b.age = 22;

state.a === copy.a // true 基本数据类型是值比较
state.b === copy.b // true 浅比较,因为引用相同
// 搜索组件:用户在【输入框+'筛选'按钮】组件中输入内容时不更新选项列表组件,只有当点击'筛选'按钮时才更新

class ProductItem extends Component {
  shouldComponentUpdate(nextProps, nextState) {
// 只有当点击'筛选'按钮等使this.props.list发生改变时才会update此组件(input时不会)
// 此时nextProps是最新的props数据,this.props.list是上一次的数据。但是render()里的this.props.list也是最新的。
    if (nextProps.list === this.props.list) {
      return false; // 不更新
    } else {
      return true; // 更新
    }
  }

  render() {...}
}

我的快速小结react生命周期

1.创建时:

constructor()
getDerivedStateFromProps()
render()——> 生成虚拟DOM ——> 生成真实DOM ——>
react更新DOM
componentDidMount

2.更新时:

props、state改变(setState()),forceUpdate()
getDerivedStateFromProps() [dɪ'raɪvd]
shouldComponentUpdate ——> true ——>
render()——> 生成虚拟DOM ——>对比此次的虚拟DOM和上一次的虚拟DOM——> 找到区别 ——>
react更新真实DOM
componentDidUpadate

3.卸载时

componentWillUnmount


添加新评论