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

因为我之前学习过Vue,我可以将React中的生命周期与Vue联合在一起,所谓生命周期,它都是一个过程,一些函数,它所代表的是组件从创建到死亡它会经历一些特定的阶段React,组件中包含一系列钩子函数(生命周期回调函数), 会在特定的时刻调用。我们在定义组件的时候,可以在这些特定的生命周期回调函数中,做特定的工作,如:请求数据,修改数据,数据校验等….

简单来讲,React中的生命周期基本可以分为三个阶段: 初始化阶段 –> 数据更新阶段 –> 组件卸载阶段
image

生命周期(旧)

1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1.constructor()
2.componentWillMount()
/*
组件初始化时只调用,以后组件更新不调用,
整个生命周期只调用一次,此时可以修改state。
在渲染前调用,在客户端也在服务端。(后续可能会被弃用)
*/
3.render() 常用
/*
react最重要的步骤,创建虚拟dom,
进行diff算法,更新dom树都在此进行。
此时就不能更改state了。
*/
4.componentDidMount() 常用
/*
一般在这个钩子中做一些初始化的事情,
比如开启定时器、发送网络请求、订阅消息
*/

2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
1.shouldComponentUpdate(nextProps, nextState)
/*
react性能优化非常重要的一环。组件接受新的state或者props时调用,
我们可以设置在此对比前后两个props和state是否相同,
如果相同则返回false阻止更新,因为相同的属性状态一定会生成相同的dom树,
这样就不需要创造新的dom树和旧的dom树进行diff算法对比,
节省大量性能,尤其是在dom结构复杂的时候
返回一个布尔值。在组件接收到新的props或者state时被调用。
在初始化时或者使用forceUpdate时不被调用。
可以在你确认不需要更新组件时使用。
*/
2.componentWillReceiveProps()(用于子组件)
/*
组件初始化时不调用,组件接受新的props时调用。
使用componentWillReceiveProps的时候,不要去向上分发,
调用父组件的相关setState方法,否则会成为死循环
在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。
可以得到旧数据和新数据
*/
3.componentWillUpdate()
/*
内置两个形参,旧的props以及旧的state
组件初始化时不调用,只有在组件将要更新时才调用,此时可以修改state,但是不能调用setState,否者会死循环
因为每一次调用setState都会使数据数显,数据刷新就会调用componentWillUpdate,死循环
*/
4.render() 数据刷新,页面重新渲染
5.componentDidUpdate(preProps,preState)
// 组件初始化时不调用,组件更新完成后调用,此时可以获取dom节点。

3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1.componentWillUnmount() 常用
/*
组件将要卸载时调用,一些事件监听和定时器需要在此时清除。
一般在这个钩子中做一些收尾的事情,
比如:关闭定时器、取消订阅消息
*/

案例展示

  • 要求:父子组件间传值,子组件可以通过输入框来提交数据给父组件,父组件进行数据展示,但是要通过生命周期的进行数据判断,如果传相同的值则不再重新渲染页面,在父组件中的数据展示的样式是以定时器的方式渐变淡,再变深,点击销毁生命周期来停止定时器
// 旧版本生命周期案例展示:

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: componentWillMount(此时页面即将挂载)
UNSAFE_componentWillMount(){
// 开启定时器是数据渐变展示:
this.timer = setInterval(()=>{
if(this.state.opacity >= 0){
this.setState({
opacity : this.state.opacity - 0.1
})
}else{
this.setState({
opacity : 1
})
}
},100)
}

// 更新时:生命周期2: componentWillReceiveProps():父组件属性修改时并引发子组件修改时触发
UNSAFE_componentWillReceiveProps(nextProps){
// 可以得到旧数据以及新数据
console.log('[更新]componentWillReceiveProps:子组件接收到父组件传过来的新数据:',nextProps);
console.log('[更新]componentWillReceiveProps:子组件接收到父组件传过来的旧数据:',this.props.sendName);
// 在这里可以做数据的逻辑转化加工等
this.setState({
name:nextProps.sendName+'生命周期加工'
})
}

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

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

// 父组件
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: componentWillMount(此时页面即将挂载)
UNSAFE_componentWillMount(){
console.log('[初始化]componentWillMount,此时可以读取state中的数据:',this.state.name);
console.log('[初始化]componentWillMount,但是无法获取dom结构',document.querySelector('#myname'));
}

// 初始化:生命周期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('数据更新的最新数据',nextState);
if(this.state.name === nextState.name){
// 做数据判断(最新的输入数据与原来的数据相同页面不用重新渲染)
console.log('[更新]shouldComponentUpdate:最新的输入数据与原来的数据相同,页面不用重新渲染!');
return false
}
console.log('[更新]shouldComponentUpdate:最新的输入数据与原来的数据不同,页面需要重新渲染!');
return true
}

// 更新时: 生命周期3: componentWillUpdate(nextProps, nextState)(数据即将更新,此时获取到Dom中的数据为旧数据)
UNSAFE_componentWillUpdate(nextProps, nextState){
// 这个钩子不常用,内置两个形参来获取到最新的数据(props或者state)
console.log('[更新]componentWillUpdate:新的数据>',nextState);
}

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

结果展示:

image

重要的生命周期函数

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

即将废弃的生命周期函数

问题所在:

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

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