玩转Reactjs第四篇-组件(生命周期)

2024-03-20 10:18

本文主要是介绍玩转Reactjs第四篇-组件(生命周期),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、前言

     前一篇主要介绍了Reactjs组件的创建模式,了解了state以及props的基本知识和用法。本篇将重点介绍下Reactjs的生命周期这对于我们认识Reactjs的实现原理,正确使用钩子函数至关重要。

   Reactjs在V16.3版本之前使用的是旧版本的生命周期,之后启用了新版的生命周期,V17版本将彻底废弃旧版本。我们将两个版本都介绍下。

二、旧版本

     Reactjs生命周期包括挂载时(Mounting),更新时(Updateing),卸载时(Unmounting)三种场景,每种场景在不同状态提供相关的钩子函数,我们可以使用这些钩子函数,实现自定义功能。

生命周期钩子函数时序的示意图如下:

1、挂载时(Mounting)

Reactjs组件首次加载时执行的流程,constructor->componentWillMount->render->componentDidMount。

  • constructor

调用时机:组件挂载前会调用构造函数。

钩子作用:前一篇我们也讲到,在构造方法里,主要做以下两件事:

(1)初始化state,这里是唯一一个可以使用this.state进行初始化的地方,其他的地方只能使用setState更新。

(2)组件内定义的方法绑定实例,前一篇也讲到如果不用箭头函数定义方法,必须在构造器中绑定this。

如果不需要使用以上两点,可以不定义组件的构造函数。

函数示例:示例如下:

constructor(props){// 1、调用父类构造方法super(props);//2、初始化state对象this.state = {hotItem:['口罩','手套','酒精',]};//绑定thisthis.changeHot = this.changeHot.bind(this);
}
  • componentWillMount(即将废弃)

在方法即将废弃,可以使用到React 17版本,但是会和新的组件生命周期钩子函数冲突,建议不要使用。

调用时机:在调用render()前调用。

钩子作用:在该方法里面调用setState不会触发额外的渲染,所以想要在该方法中请求外部数据,减少一次render,是一个常见的误区,总的来说该方法没有太大的作用。

  • render

调用时机:首次挂载以及更新时,都会调用该方法进行渲染。

钩子作用:render方法是class组件中唯一需要实现的方法。在该钩子方法里,将React JSX渲染成DOM节点,并挂载到DOM树中。

render函数应该是纯的,意味着不应该改变组件的状态,其每次调用都应返回相同的结果,同时它不会直接和浏览器交互。

函数示例:该方法的实现形式,前一篇我们也重点介绍了,如App.js中,实例如下:

render(){return (<div className="App">{/* 引入组件 */}<SearchBox changeSearchVal = {this.changeSearchVal}/><SearchHot /><SearchList searchListVal={this.state.searchListVal}><div>为您搜索到2条记录</div></SearchList></div>);}
  • componentDidMount

调用时机:执行完render函数(DOM节点已创建完成并挂载到DOM数中(节点插入DOM数)),立即执行。

钩子作用:在componentDidMount中可以进行以下的操作:

(1)请求网络数据,并调用setState进行更新,它将会触发一次额外的渲染,但是它将在浏览器刷新屏幕之前发生。这保证了在此情况下即使render()将会调用两次,用户也不会看到中间状态

(2)通过ref获取DOM的节点,获取DOM属性或者操作DOM。

函数示例:在App.js中,我们增加componentDidMount方法,在该钩子函数中获取默认的搜索列表,这样就能实现在首次加载完成后,页面展示默认列表。

componentDidMount(){console.log("App componentDidMount");fetch("http://localhost:3000/search.json",{method:'GET',}).then(response => response.json()).then((data)=>{this.setState({searchListVal:data.search});})}

2、更新时(Updateing)

挂载完成后,当传入父组件的props,以及组件内的state值发生变化,会触发更新时流程。componentWillReceiveProps->shouldComponentUpdate->componentWillUpdate->render->componentDidUpdate

  • componentWillReceiveProps(nextProps)(即将废弃)

该钩子函数将在React 17版本废弃。

调用时机:在已挂载的组件接收新的 props 之前被调用。

钩子作用:当接收到新属性后,通过if比较新旧Props不同,此方法中使用 this.setState() 执行 state 转换。这个和新版本的getDerivedStateFromProps作用类似。

  • shouldComponentUpdate(nextProps,nextState)

调用时机:props或者state发送变化后,render调用前调用该钩子函数。父组件更新prop,或者调用setState都会触发。但是首次挂载以及forceUpdate不会触发该方法。

钩子作用:该函数可以决定是否继续渲染(是否执行render方法),入参为新的props和state,返回的是个布尔值,true表示继续渲染,false则表示阻止渲染,默认返回true。

     在Vue中,通过监听相关属性值的变化,通过机制自动判断是否需要渲染,无法"人为"干预。在Reactjs中,利用shouldComponentUpdate为我们提供了干预的契机,进行精确控制。

适用场景:在某些状态或属性变化时,通过this.props和nextProps以及this.state 和 nextState比较,来判断是否需要重新渲染该组件。

函数示例:我们来改写下SearchHot.js,当热搜的个数有变化时,返回false,不继续渲染。

shouldComponentUpdate(nextProps,nextState){console.log(nextState.hotItem.length);if(this.state.hotItem.length != nextState.hotItem.length){return false;}return true;}

由于state.hotItem新旧值的长度不一致,所以点击"下一批",页面不会有变化。

需要注意的是,state和props需要保持结构简单,层次不要太深,不建议在 shouldComponentUpdate() 中进行深层比较或使用 JSON.stringify()。这样非常影响效率,且会损害性能。

如果仅做浅层次比较,可以考虑使用React.PureComponent。

  • componentWillUpdate(nextProps, nextState)(即将废弃)

该钩子函数将在React 17版本废弃,

调用时机:当组件收到新的 props 或 state 时,会在渲染之前调用。

钩子作用:入参为新的props以及state,使用此作为在更新发生之前执行准备更新的机会,初始渲染不会调用此方法。

适用场景:可以读取DOM属性,为componentDidUpdate作准备(使用场景少)。不能在此方法中调用 this.setState()

  • componentDidUpdate(prevProps, prevState, snapshot)

调用时机:在组件已经重新渲染之后调用,首次渲染不会执行此方法。

钩子作用:入参为前props,state值,第三个参数与新版本getSnapshotBeforeUpdate有关,下一节介绍。其作用类似componentDidMount,此时已经挂载完成,可以做DOM进行操作,也可以进行网络请求。

使用场景:对DOM进行操作,比如滚动条指定到 位置,获取DOM尺寸等。在该方法里,可以调用setState,但是要包裹在一定的条件中,否则会 引起死循环。调用setSate会引起再一次的渲染,虽然用户看不见(此时浏览器还没有变化),但是会影响性能。

函数示例:   我们改写下 SearchList.js,每次更新完,滚动条指向列表的指定位置。(这里使用了ref获取dom对象,后面会专门介绍)

componentDidUpdate(prevProps, prevState, snapshot){console.log("componentDidUpdate");//列表的滚动条指向60的位置this.searchul.current.scrollTop=60;}
...
render(){...<ul className="u" ref={this.searchul}>
...
}

3、卸载时(Unmounting)

这个阶段是指组件将被删除并从DOM中清除。

  • componentWillUnmount()

调用时机:在组件卸载及销毁之前直接调用。

适用场景:此方法中执行必要的清理操作,例如,清除 timer,取消网络请求。不要在此方法中调用setState,因为该组件永远不会渲染。

三、新版本

V16.3版本启用了新版本生命周期钩子函数,不过此版本还可以兼容老版本的钩子函数,与老版本一样,也分三种场景。

生命周期的钩子函数时序的示意图如下:

与老版本比较,钩子函数发送如下变化:

删除:componentWillMount,componentWillReceiveProps,componentWillUpdate

新增:getDerivedStateFromProps,getSnapshotBeforeUpdate。

其他的函数保留。我们重点介绍下新增的。

  • static getDerivedStateFromProps(nextProps,preState)

调用时机:props以及state发送更新后,会在调用 render 方法之前调用发。与旧的生命周期图比较可以看出,getDerivedStateFromProps处于原来的componentWillMountcomponentWillReceiveProps位置,setState以及forceUpdate方法的调用都会触发该钩子。

钩子作用:从钩子的函数名称可以看出,用接受的props值来改变state值,返回一个新的state。它的入参是新props,旧state,该函数为一个静态的,这就表示无法使用组件实例(无法调用this)。

适用场景:组件分为受控和非受控组件,受控是指数据由父组件的props传入的组件(可以由父组件控制),非受控是指数据保存在内部的state的组件(外部无法直接控制)。如果state可以由props派生出,那么就意味着该组件既是受控的,又是非受控的。其数据源就不唯一了,会导致一些意想不到的问题。通过props派生出state,是我们尽量需要避免的情况

官方文档中,也为我们罗列了以下两种常见场景的替代方案,在你可能不需要使用派生 state这篇博文中进行了详细的介绍。

(1)如果只想在 prop 更改时重新计算某些数据,请使用 memoization helper 代替。
(2)如果你想在 prop 更改时“重置”某些 state,请考虑使组件完全受控或使用 key 使组件完全不受控 代替。

函数示例:SearchList组件中,搜索的结果列表都是由父组件App通过props传给该组件,下面我们修改下,在SearchList定义一个默认列表保存到state中,当父组件传入的prop的列表结果更新后,修改state的值,实现更新。

constructor(props){super(props);this.searchul = React.createRef();//定义默认搜索列表this.state={searchListVal:['电脑','电视','冰箱']};}static getDerivedStateFromProps(nextProps,preState){//当props的列表发送更新,且与state的列表不一致,且不为空if(nextProps.searchListVal!=preState.searchListVal&&nextProps.searchListVal.length > 0){//修改state的searchListVal值,返回新值return{searchListVal:nextProps.searchListVal}}//其他更新,state不变化return null;}
  • getSnapshotBeforeUpdate(prevProps, prevState)

调用时机:在最近一次渲染输出(提交到 DOM 节点)之前调用。

钩子作用:它让你的组件能在当前的值可能要改变前获得它们。这一生命周期返回的任何值将会 作为参数被传递给componentDidUpdate(),即snapshot入参。

适用场景:适用的场景也不常用,它可能出现在 UI 处理中,如需要以特殊方式处理滚动位置的聊天线程等。

函数示例:在SearchList组件中,我们通过componentDidUpdate,每次加载列表,将滚动条滚动到指定的位置。下面我们再次修改下,获取滚动条之前的位置,在此基础上再累加一个指定值,模拟懒加载的情况下,每次显示最新加载的值(列表底部)。

getSnapshotBeforeUpdate(prevProps, prevState){//获取当前滚动条位置return this.searchul.current.scrollTop;}componentDidUpdate(prevProps, prevState, snapshot){//在滚动条位置上,累加60个if(snapshot!=null){this.searchul.current.scrollTop=snapshot+60;} }

四、与Vue的生命周期比较

这里我们将Reactjs的生命周期与Vue的比较下。

1、挂载时

2、更新时

3、销毁时

五、总结

本章节主要描述了Reactjs的新旧生命周期以及相关钩子函数的调用时机,功能,以及适用场景,并与vue的生命周期做了比较。

老版本的生命周期钩子函数的时序:

1、挂载时,constructor->componentWillMount->render->componentDidMount

2、更新时,componentWillReceiveProps->shouldComponentUpdate->componentWillUpdate->render->componentDidUpdate

3、卸载时,componentWillUnmount

新版本的生命周期钩子函数的时序:

1、挂载时,constructor->getDerivedStateFromProps->render->componentDidMount

2、更新时,getDerivedStateFromProps->shouldComponentUpdate->render->componentDidUpdate

3、卸载时,componentWillUnmount

这篇关于玩转Reactjs第四篇-组件(生命周期)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/829142

相关文章

Vue中组件之间传值的六种方式(完整版)

《Vue中组件之间传值的六种方式(完整版)》组件是vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用,针对不同的使用场景,如何选择行之有效的通信方式... 目录前言方法一、props/$emit1.父组件向子组件传值2.子组件向父组件传值(通过事件形式)方

Spring组件初始化扩展点BeanPostProcessor的作用详解

《Spring组件初始化扩展点BeanPostProcessor的作用详解》本文通过实战案例和常见应用场景详细介绍了BeanPostProcessor的使用,并强调了其在Spring扩展中的重要性,感... 目录一、概述二、BeanPostProcessor的作用三、核心方法解析1、postProcessB

kotlin中的行为组件及高级用法

《kotlin中的行为组件及高级用法》Jetpack中的四大行为组件:WorkManager、DataBinding、Coroutines和Lifecycle,分别解决了后台任务调度、数据驱动UI、异... 目录WorkManager工作原理最佳实践Data Binding工作原理进阶技巧Coroutine

Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)

《Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)》文章介绍了如何使用dhtmlx-gantt组件来实现公司的甘特图需求,并提供了一个简单的Vue组件示例,文章还分享了一... 目录一、首先 npm 安装插件二、创建一个vue组件三、业务页面内 引用自定义组件:四、dhtmlx

Vue ElementUI中Upload组件批量上传的实现代码

《VueElementUI中Upload组件批量上传的实现代码》ElementUI中Upload组件批量上传通过获取upload组件的DOM、文件、上传地址和数据,封装uploadFiles方法,使... ElementUI中Upload组件如何批量上传首先就是upload组件 <el-upl

Vue3中的动态组件详解

《Vue3中的动态组件详解》本文介绍了Vue3中的动态组件,通过`component:is=动态组件名或组件对象/component`来实现根据条件动态渲染不同的组件,此外,还提到了使用`markRa... 目录vue3动态组件动态组件的基本使用第一种写法第二种写法性能优化解决方法总结Vue3动态组件动态

四种Flutter子页面向父组件传递数据的方法介绍

《四种Flutter子页面向父组件传递数据的方法介绍》在Flutter中,如果父组件需要调用子组件的方法,可以通过常用的四种方式实现,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录方法 1:使用 GlobalKey 和 State 调用子组件方法方法 2:通过回调函数(Callb

Vue项目中Element UI组件未注册的问题原因及解决方法

《Vue项目中ElementUI组件未注册的问题原因及解决方法》在Vue项目中使用ElementUI组件库时,开发者可能会遇到一些常见问题,例如组件未正确注册导致的警告或错误,本文将详细探讨这些问题... 目录引言一、问题背景1.1 错误信息分析1.2 问题原因二、解决方法2.1 全局引入 Element

关于Maven生命周期相关命令演示

《关于Maven生命周期相关命令演示》Maven的生命周期分为Clean、Default和Site三个主要阶段,每个阶段包含多个关键步骤,如清理、编译、测试、打包等,通过执行相应的Maven命令,可以... 目录1. Maven 生命周期概述1.1 Clean Lifecycle1.2 Default Li

vue解决子组件样式覆盖问题scoped deep

《vue解决子组件样式覆盖问题scopeddeep》文章主要介绍了在Vue项目中处理全局样式和局部样式的方法,包括使用scoped属性和深度选择器(/deep/)来覆盖子组件的样式,作者建议所有组件... 目录前言scoped分析deep分析使用总结所有组件必须加scoped父组件覆盖子组件使用deep前言