React小书学习笔记 —— 挂载阶段的组件生命周期(二)
前言
学习前端一小步,迈向成功一大步!本专栏主要记录学习前端React框架的一些个人心得,分享一些实战教学,如有不足,欢迎交流讨论。React框架的入门教学强推胡子大哈的React小书,简单易懂还有代码实战。还等什么?让我们开始本篇的前端学习之旅,欢迎各位入坑前端!
挂载阶段的组件生命周期(二)
参考教程:React小书–第18节(挂载阶段的组件生命周期(二))
教程作者:胡子大哈
参考链接:React小书
本文搭配原文教程食用,风味更佳~!
经过上一节的讲述,你大概已经知道挂载阶段组件各生命周期有哪些,包括其排布的方式,调用的顺序等等,这节我们将接下来将讨论对于一个组件来说,其挂载阶段生命周期的几个方法(constructor ; componentWillMount; componentDidMount; componentWillUnmount)在一个组件的出生到死亡的过程里面起了什么样的作用。
constructor()
一般来说, constructor 里主要做一些关于组件自身状态的初始化工作。即所有组件的 state 的初始化工作都是放在 constructor 里面的,具体代码如下:
1 |
|
componentWillMount()
小书中提到,componentWillMount 生命周期主要用于一些组件启动的动作,比如Ajax 数据的拉取操作、一些定时器的启动等,此外,书中还举了一个Ajax的例子:
1 |
|
但是!问题来了: 上一节我们不是说componentWillMount是在render前执行,无法触发setState的二次渲染吗?那这例子又是什么例子呢?对此,这里引出另一篇参考文献(React componentwillmount和componentdidmount请求数据)来深入了解一下。
首先要明确一点,在componentWillMount中执行this.setState是不会触发二次渲染的。 它也只会在挂载过程中被调用一次,它的作用和constructor没有太大差异。
在componentWillMount中请求后台数据是无法保证更早得到数据的,因为componentWillMout是在render函数执行前执行的,虽然请求是在第一次render之前发送的,但是返回并不能保证在render之前完成(即按照js异步的特点,在请求数据时,render已经开始异步工作了),render不会等你慢慢请求.所以在渲染的时候没有办法等到数据到来再去setState触发二次渲染。
那为什么小书中说可以用 componentWillMount 进行 Ajax 数据拉取的操作呢?这是因为在服务端渲染的场景中componentDidMount是不会被执行的,因此可以在componnetWillMount中发送AJAX请求,注意这里是发送请求,而非发送并接收到请求,举个例子:你尝试过自己给自己送信吗?如果你要自己给自己送信会怎么办?方法很简单,你写一封信从浙江寄到武汉,寄完马上乘动车从浙江出发,到达武汉后等着这封信送到你手中就可以了,当然这封信可能会比你早到,那么你去邮局取件完事。componentWillMount 和 render 的关系也是如此,componentWillMount 先发送请求,然后程序就开始执行 render,至于请求怎么处理的和 render 执行不存在冲突,render 完成后等待请求结果或者获取结果即可。
顺便说一句在es6中,使用extend component的方式里的constructor函数和componentWillMount是通用的作用,所以你在构造函数里初始化了组件的状态就不必在WillMount做重复的事情了.React中不推荐在componentWillMount中发送异步请求。
以下是对该片引文的重点提取。
- componentWillMount 是一个同步操作,即你只有进行了 componentWillMount 才能进行后续的 render 等一系列操作。
- componentWillMount 在 render 前执行,所以 componentWillMount 中执行 this.setState 无法触发二次渲染(此时组件自身都没被渲染)。
- componentWillMount 可以执行一些不依赖于组件渲染的操作,比如定时器启动,请求发送等。
引文还将 componentWillMount 和 componentDidMount 做了对比,相比于 componentWillMount ,componentDidMount 这个生命周期函数在是在render之后调用一次,component已经初始化完成了。在生产时,componentDidMount生命周期函数是最好的时间去请求数据,其中最重要原因:使用componentDidMount第一个好处就是这个一定是在组件初始化完成之后,再会请求数据,因此不会报什么警告或者错误,我们正常请教数据完成之后一般都会setState。
componentWillUnmout()
componentWillUnmount 生命周期主要在组件销毁前(注意是销毁前,可以看前一篇,是先执行 componentWillUnmout,后在页面删除组件),执行一些清场工作,比如清楚定时器等。在 React 中,组件生命周期是 js 自己控制的,父组件不要子组件了,那么子组件就可以卸载了,就这么简单。React 管理 DOM 的过程,可以理解为每个组件 render() 返回的虚拟 DOM 会被 React 整理到一个树里面,按照它们之间相互依赖的关系,把相应的组件 mount 起来。然后可能父组件状态变化之后, render() 不返回某个子组件了,那么这个子组件就会被 React unmount 掉。
动态时钟实战
下面记录了不堪入目的代码实战过程:
step1:创建React
1 |
|
step2:定义时钟组件
创建 Clock.js (注意大写,语法规范:导出类名的文件开头首字母大写)
编写代码:
1 |
|
稍作解释:
Clock.js 中首先要引入import React,{Component} from 'react';
(基本每个React组件都要引用),创建组件,在组件构造函数生命周期中初始化 state 状态(初始化一个对象),new Date()
定义了一个 Date 对象,在 render 中调用 Date 对象的.toLocaleTimeString()
方法,根据本地时间格式,把 Date 对象的时间部分转换为字符串。可结合“Date 对象”一起学习。最后 export default xxx
导出组件类
index.js 主要包含了 ReactDOM.render()
方法,需要 import React from 'react';import ReactDOM from 'react-dom';import Clock from './Clock';
,然后导入组件进行渲染。
ok! npm start
运行~
what!!!出错了,好像render()没有return,大意了。加上return后再运行(注意return后加 ()
)。
1 |
|
step3:实现时间动态:
完美!但是现在时间是静态的,我们需要加上一个定时器,放在 componentWillMount() 生命周期中,每隔多少秒就重新更新一次state状态(实际操作过程中,发现vscode竟然没有自动提示componentWillMount(),果然是被淘汰了呀,不过幸好还能用)。
上述意思大致是:componentWillMount 被重命名为了 UNSAFE_componentWillMount,React 建议少用 componentWillMount,因为初始化状态可以在 constructor 中进行,其余的可以在 componentDidMount 中进行,所以没 componentWillMount 啥事了。
扯远了,继续撸代码,上步我们加了一个 componentWillMount,里面装了个定时器setInterval(),每个1000ms重新更新一下状态并渲染(this.setState),这里由于每秒都会渲染一次,所以不用担心componentWillMount 无法二次渲染的问题。
1 |
|
step4:时间隐藏与显示
至此我们实现了时钟的动态展示,下一步我们实现时间的隐藏:
在 index.js 中新建一个 Index 类,用来包裹 Clock 组件,并设置一些隐藏的功能。代码如下:
1 |
|
需要注意的几点:
- this.setState()是一个方法,传入一个state对象,即用新的state更新上一时间的state状态(注意是更新而非替换,若新的状态不在原来的状态内,则是添加操作)。
- 组件中调用状态值都要用
this.state.属性名
的形式。 - 关注
<div>{...}</div>
的形式,这是典型的JSX书写,即HTML中嵌入JS代码。任何函数及表达式在JSX中都要写入{}
内,例如onClick={...函数...}
- 注意
.bind(this)
,组件内部函数没有绑定 this 对象,所以需要人为绑定。箭头函数例外,箭头函数会自动指向 this。
更新完之后,我们运行代码:
step5:添加componentWillUnmout,清除定时器
隐藏和显示的功能我们已经实现了,但是F12调试发现居然报错了!
报错原因大致是:没有 unmount 的组件,容易造成内存泄露。结合小书学习我们可知,定时器在每次组件移除时并没有被清除,下一次组件生成时又创建了一个新的定时器,时钟隐藏的时候,定时器的回调函数还在不停地尝试 setState,由于 setState 只能在已经挂载或者正在挂载的组件上调用,所以 React.js 开始疯狂报错。这时候componentWillUnmount 就可以派上用场了,它的作用就是在组件销毁的时候,做这种清场的工作。例如清除该组件的定时器和其他的数据清理工作。我们给 Clock 添加 componentWillUnmount,在组件销毁的时候清除该组件的定时器:
1 |
|
注意:这行代码是写在 Clock.js 下的。
至此,我们完成了实战,时间能动态走动,且能隐藏或显示,并且不会报错。
总结
我们一般会把组件的 state 的初始化工作放在 constructor 里面去做;
在 componentWillMount 进行组件的启动工作,例如 Ajax 数据拉取、定时器的启动;
组件从页面上销毁的时候,有时候需要一些数据的清理,例如定时器的清理,就会放在 componentWillUnmount 里面去做。
没有提到的 componentDidMount 将在后续讲解。一般来说,有些组件的启动工作是依赖 DOM 的,例如动画的启动,而 componentWillMount 的时候组件还没挂载完成,所以没法进行这些启动工作,这时候就可以把这些操作放在 componentDidMount 当中。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!