React学习笔记 - 生命周期中使用setState以及生命周期中的性能优化

1. 生命周期中使用setState

  • 一般来讲是不推荐在生命周期钩子中使用setState,因为每一次使用setState就意味着数据更新,页面的重新渲染,页面重新渲染就会使得某些生命周期重新执行一遍,也就是说,在某些生命周期钩子(componentWillUpdate,shouldComponentUpdate)中使用setState会使组件陷入死循环,而新的生命周期钩子中(getSnapshotBeforeUpdate,以及getDerivedStateFromProps)则无法使用setState,因为这两个钩子都需要静态引用(加上前缀static),也就是说这两个钩子没有this指向,但是他们提供了一些形参来让你获取最新的state或者props数据

因此要想了解在生命周期中如何使用setState,就需要下面的分析了!

一. 首先我们需要了解一下React生命周期的成员以及它们的执行流程:

image

React生命周期可以分为两条执行流程:

  1. 初始化:初始进入页面 → constructorcomponentWillMount(getDerivedStateFromProps[新]) → render → componentDidMountcomponentWillUnmount

  2. 数据更新setStatecomponentWillReceiveProps(getDerivedStateFromProps[新]) → shouldComponentUpdatecomponentWillUpdate[旧,后续可能会被取消] → rendergetSnapshotBeforeUpdate[新,取代componentWillUpdate]componentDidUpdatecomponentWillUnmount

    • 更新流程需要提到两点:一点是componentWillReceiveProps中使用setStatestate会被收集储存起来,这里是区别于上面componentWillMountstate合并到初始数据中的。这里提一下:shouldComponentUpdate如果return => false,则不会执行更新(render)。

1. 初始化

image

1、首先,进入页面,会初始化页面数据(state, props, context等…),等待备用;

2、然后,设置 生命状态 为:MOUNTING,这个状态下面会说明它的用途,这里我们先按照流程继续往下走;

3、接下来,在componentWillMount中,setState操作,只是把state合并到初始化状态中,而根本不会触发render 在这里更新state,就等同于直接写在this.state中,所以,在此生命周期中的setState根本没有意义

4、执行到这里,生命状态 会被重置为 null,之后是渲染页面(即执行render);

5、最后,渲染完以后,执行componentDidMount,这里使用setState即会正常触发重新渲染了,更新state。(接下来,就是更新流程了!!)

2. 数据更新

image

1、首先,react会比较前后元素、状态等是否不同,如果不同则正式发起更新;

2、然后,生命状态 被设置为RECEIVE_PROPS(注意:此时生命周期中,setState不会触发更新,而是会做其他处理);

3、接下来,componentWillReceiveProps中的setState就不会执行更新,而是合并挂载起来,等待render时统一更新;

4、到这里,生命状态 会重置为null;然后shouldComponentUpdate中会判断是否更新;之后是componentWillUpdate

注意:
  • shouldComponentUpdatecomponentWillUpdate执行的时候,生命状态 已经被重置为null,在它们里面的setState会触发更新,那么在其间使用呢?会造成什么?答案就是:在一个更新周期还没有render之前,再次发起updateComponent,直接导致递归更, 也就是死循环,由此可见,在这两个生命周期钩子中是禁止使用setState

5、最后,渲染页面;再执行componentDidUpdate;它里面执行setState,会触发更新,不同的是render完成之后再发起的reRender。虽然这儿区别于上面两个生命周期中使用的情况,但是会一遍一遍的更新,这肯定也是不合理的,所以需要有条件的使用setState

总结:

生命周期中setState的使用情况:

  1. 无意义使用:componentWillMountcomponentWillUnmount
  2. 有条件使用:componentDidUpdate
  3. 禁止使用:componentWillUpdateshouldComponentUpdate
  4. 正常使用:componentWIllReceivePropscomponentDidMount

生命周期中setState是否触发更新:

  • componentWillMountcomponentWillReceiveProps中,setState会被react内部处理,而不触发render;其他生命周期均正常出发更新渲染。

生命周期中的性能优化

  • 一般看来讲,生命周期中的性能优化一般有两种方法:第一种是手动优化,就是之前博客提到的生命周期钩子中,shouldComponentUpdate的使用,通过我们手动对比判断通信传递中的状态数据是否前后一致,一致则组件不需要重新渲染一边
// 更新时:生命周期1: shouldComponentUpdate(nextProps, nextState):用于性能优化
shouldComponentUpdate(nextProps, nextState){
/*
在这个生命周期中,我们可以获取更新的最新数据,并在这里做性能优化,要知道
基本上每一次数据的更新都会需要用到setState函数,这也就说明了每一次的数据
更新基本都需要重新渲染页面,那要是数据更新前与更新后一致呢?这就没有重新渲染
页面的需要了,这就引出了shouldComponentUpdate()这个生命周期,这个生命周期需要
有一个返回值,且返回值为布尔值,用于通知react是否需要做页面的重新渲染,我们可以在
这里做数据的校验判断,当新旧数据一致时,就返回false(不需要渲染),反之则返回true
*/
console.log('[更新]shouldComponentUpdate:数据更新的最新数据',nextState);
if(this.state.name === nextState.name){
// 做数据判断(最新的输入数据与原来的数据相同页面不用重新渲染)
console.log('[更新]shouldComponentUpdate:最新的输入数据与原来的数据相同,页面不用重新渲染!');
return false
}
console.log('[更新]shouldComponentUpdate:最新的输入数据与原来的数据不同,页面需要重新渲染!');
return true
}
  • 第二种就是React中内置的智能对比传输前后数据状态,PureComponent,它会自动的帮你 比较新props 跟 旧的props, 新的state和老的state(值相等,或者对象含有相同的属性、且属性值相等 ),决定shouldcomponentUpdate 返回true 或者false, 从而决定要不要呼叫 render function。但是要注意如果你的你的 stateprops永远都会变』,那 PureComponent 并不会比较快,因为shallowEqual 也需要花时间。
// 使用方法,在引入React组件时直接引入PureComponent取代Component即可

import React, { PureComponent } from 'react'//引入PureComponent取代Component

export default class App extends PureComponent {
render() {
return (
<div>App</div>
)
}
}

// 这样就相当于设置了shouldComponentUpdate中的数据对比操作