React学习笔记 - 生命周期(新)

生命周期(新)

生命周期的三个阶段(新)
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1.constructor()
2.getDerivedStateFromProps(替换了旧componentWillReceiveProps)
/*
第一次的初始化组件以及后续的更新过程中(包括自身状态更新以及父传子) ,
返回一个对象作为新的state,返回null则说明不需要在这里更新state
也就是说组件的每一次创建到数据更新都会走这个钩子,除了销毁
且在这个钩子中不能使用setstate否者会造成死循环
这个钩子需要静态调用(static)且该组件下需要有状态(state)以及该钩子需要有返回值
*/
3.render()
4.componentDidMount()

2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
1.getDerivedStateFromProps()//[替换了旧componentWillReceiveProps]
2.shouldComponentUpdate()
3.render()
4.getSnapshotBeforeUpdate()
/*
取代了 componetWillUpdate ,触发时间为update发生的时候,
在render之后dom渲染之前返回一个值,作为componentDidUpdate的第三个参数。
*/
5.componentDidUpdate(preProps,preState,value)
// value为getSnapshotBeforeUpdate记录的数据,例如用于记录dom的状态等

3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1.componentWillUnmount()

image

案例展示

  • 与上一版本的案例基本一致, 但是除去了渐变展示功能,因为如果使用渐变展示在生命周期中,每一次使用setState来更改透明度都会引起state中的数据更新,这就会导致getDerivedStateFromProps,componentDidUpdate等生命周期运行,但是我在案例中又经常以输出来展示对应的生命周期钩子函数的运行,因此就删除了渐变展示效果!
// 新版本生命周期案例展示:
import React, { Component } from 'react'

// 子组件
class Son extends Component{
state={
opacity:1,//页面透明度
name:"lam"
}
render() {
return (
<div>
<p>-------------子组件---------------</p>
<p>
父组件传过来的名字:
<span style={{opacity:this.state.opacity}}>
{this.props.sendName}-{this.state.name}
</span>
</p>
</div>
)
}

// 初始化:生命周期1: getDerivedStateFromProps(此时页面即将挂载)
static getDerivedStateFromProps(nextProps, nextState){
// console.log('[更新]getDerivedStateFromProps:子组件接收到父组件传过来的新数据:',nextProps);
return {
name:nextProps.sendName+'生命周期加工'
}
}

// 更新时: 生命周期4: getSnapshotBeforeUpdate(): 取代了componentWillMount
// 因为componentWillMount与componentDidMount相差太远,数据记录可能不准确
getSnapshotBeforeUpdate(){
console.log('[更新]:getSnapshotBeforeUpdate');

// 比需要有返回值,作为componentDidUpdate的第三个参数
return {
data:'我在getSnapshotBeforeUpdate记录的数据'
}
}

// 更新时: 生命周期5: componentDidUpdate(数据更新完成,此时获取到的DOM中的数据为新数据)
componentDidUpdate(preProps,preState,value){
// 可以获取旧的数据
console.log('[更新]componentDidUpdate:数据更新前为(旧数据):',preState);
console.log('[更新]componentDidUpdate:getSnapshotBeforeUpdate记录返回的数据为:',value);
console.log('[更新]componentDidUpdate:此时数据已经更新完成');
}

// 卸载:生命周期1: componentWillUnmount(): 组件将要卸载时调用,一般用于做一些收尾的事情
componentWillUnmount(){
console.log('[卸载]componentWillUnmount:组件即将被消除,关闭定时器(渐变效果)');

// 清除定时器,消除绑定在浏览器(windows)上的事件等...
}
}

// 父组件
export default class Father extends Component {
myName = React.createRef()//创建一个ref属性指向输入框
state = {
name:'lam',
isShowSon:true,//是否展示子组件
}

// 初始化:生命周期2:render(页面开始渲染,做数据填充)
render() {
console.log('[页面初始化或数据更新]:页面渲染!');
return (
<div>
<p>---------------父组件---------------</p>
<input ref={this.myName} id="myname"/>
<button onClick={this.sendSon}>提交给子组件</button>
{/* 通过标志位判断是否展示子组件 */}
{this.state.isShowSon && <Son sendName={this.state.name}></Son>}
<button onClick={this.death}>销毁(关闭渐变展示)</button>
</div>
)
}

// 发送数据函数
sendSon = ()=>{
console.log('获取到父组件的输入数据',this.myName.current.value);
this.setState({
name:this.myName.current.value,//将state中的数据修改输入数据
})
this.myName.current.value = ''//清空输入框
}

// 修改标志位卸载Son组件
death = ()=>{
this.setState({
isShowSon:false//改为不展示子组件,即销毁子组件
},()=>{
console.log('修改成功!当前子组件显示状态为:',this.state.isShowSon);
})
}

// 初始化或者数据更新:生命周期1: getDerivedStateFromProps(此时页面即将挂载)
// 新生命周期:需静态调用,且该组件需要有状态(state),需要有返回值(对象)
// 此时无法使用this.state来读取组件中的状态,因为它是静态的没有this指向,可以使用nextState来读取
static getDerivedStateFromProps(nextProps, nextState){
// 获取到最新的数据
console.log('[初始化或数据更新]getDerivedStateFromProps,此时可以读取state或props中的最新数据:',nextState);
// 组件创建时无法获取dom
console.log('[初始化]getDerivedStateFromProps,第一次创建组件时无法获取dom结构',document.querySelector('#myname'));
return {
// 在return中可以做页面渲染前状态中最后一次修改(将state中的name变为大写)
name:'Lam' + nextState.name,//其实就相当于setState的生命周期版本,智能覆盖原state中的数据,同名覆盖不同名保留
}
}

// 初始化:生命周期3: componentDidMount(此时页面挂载成功)
componentDidMount(){
console.log('[初始化]componentDidMount,此时可以获取dom结构',document.querySelector('#myname'));
console.log('[初始化]componentDidMount,此时可以发送网络请求获取数据类似Vue中的Mounted');
}

// 更新时:生命周期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
}
}

结果展示:

image

重要的生命周期函数

  1. render:初始化渲染或更新渲染调用
  2. componentDidMount: 开启监听, 发送网络请求等..
  3. componentWillUnmount: 做一些收尾工作, 如: 清理定时器

即将废弃的生命周期函数

问题所在:

  1. componentWillMount
    • ssr中 这个方法将会被多次调用, 所以会重复触发多遍,同时在这里如果绑定事件, 将无法解绑,导致内存泄漏 , 变得不够安全高效逐步废弃。
  2. componentWillReceiveProps
    • 外部组件多次频繁更新传入多次不同的 props,会导致不必要的异步请求
  3. componentWillUpdate
    • 更新前记录 DOM 状态, 可能会做一些处理,与componentDidUpdate相隔时间如果过长, 会导致 状态不太信

16.8版本后使用这些生命周期函数会出现警告,需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。

生命周期替代:

  1. getDerivedStateFromProps(nextProps, nextState)替代了[初始化]:componentWillMount, componentWillReceiveProps,省略[数据更新]:componentWillUpdate, 可认为componentWillMountcomponentWillReceiveProps的合并,他会在组件创建以及数据更新的时候调用,类似于生命周期版本的setState,返回一个对象会智能的覆盖掉原来的state(属性同名覆盖,非同名则合并),但是不会引起页面的重新渲染!

  2. getSnapshotBeforeUpdate取代了[数据更新]:componentWillUpdate