React生命周期
生命周期
: 就是指某个事物从开始到结束的各个阶段。
React生命周期
:在 React.js 中指的是组件从创建到销毁的过程,React.js 在这个过程中的不同阶段调用的函数。
作用
:通过这些函数,我们可以更加精确的对组件进行控制。前面我们一直在使用的 render 函数其实就是组件生命周期渲染阶段执行的函数
注意
:React生命周期的新旧方法之间,不可以同时存在。否则报错
生命周期函数详解
常用生命周期函数
1、挂载阶段
constructor(props)
类的构造函数,也是组件初始化函数,一般会在这个阶段做一些初始化的工作
(1) 初始化 state
(2) 处理事件绑定函数的 this
export default class Person extends Component {
constructor(props) {
super(props);
this.state = { name: 'tom', age: 18 }
this.clickFn = this.clickFn.bind(this);
}
clickFn() {
console.log(this);
this.setState({ nickname: '哈皮' });
}
render() {
let { name, nickname } = this.state;
return (
<div>
<p>{name}</p>
<p>{nickname}</p>
<button onClick={
this.clickFn
}>
点击按钮
</button>
</div>
);
}
}
render()
render 方法是 Class 组件必须实现的方法
export default class Test extends Component {
render() {
return (<div>hello,good morning</div>);
}
}
2、更新阶段
static getDerivedStateFromProps(props, state)
该方法会在 render 方法之前调用,无论是挂载阶段还是更新阶段,
它的存在只有一个目的:让组件在 props 变化时更新 state
export default class App extends Component {
state = {
address: '中国',
city: '北京'
}
static getDerivedStateFromProps(nextProps, prevState) {
//console.log(nextProps);
//console.log(prevState);
if (nextProps.city !== prevState.city) {
return {
city: nextProps.city
}
}
return null;
}
updateAddress = () => {
this.setState({
city: '天津'
})
}
render() {
let { city } = this.state;
return (
<div>
amazing
<p>{city}</p>
<button onClick={
() => {
this.updateAddress()
}}>
切换城市
</button>
</div>
);
}
}
componentDidMount()
在组件挂载后(render 的内容插入 DOM 树中)调用。
通常在这个阶段,我们可以:
(1) 操作 DOM 节点
(2) 发送请求
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
fontSize: 15,
color: 'gray'
}
}
rmColor = () => {
let r = Math.floor(Math.random() * 255);
let g = Math.floor(Math.random() * 255);
let b = Math.floor(Math.random() * 255);
return 'rgb(' + r + ',' + g + ',' + b + ')';
}
// 组件渲染后调用
componentDidMount() {
let num = 5;
// 设置定时器
let timer = setInterval(() => {
console.log(num);
let { fontSize } = this.state;
if (fontSize > 30) {
fontSize = 15;
}
// 每次自增5
this.setState({
fontSize: fontSize + 5,
color: this.rmColor()
});
num--;
if (num < 1) {
clearInterval(timer);
}
}, 500);
// 如果定时器那里不使用箭头函数,就需要在定时器中绑定this
// }.bind(this), 500);
}
render() {
return (
// 将新值赋给当前的fontSize
<div style={
{
fontSize: this.state.fontSize + 'px',
color: this.state.color
}
}>
Hello! Welcome to Beijing
</div>
);
}
}
shouldComponentUpdate(nextProps, nextState)
(1) 发生在更新阶段,getDerivedStateFromProps 之后,render 之前,
(2) 该函数返回一个布尔值,默认返回true,它决定了后续是否执行render方法
(3) 首次渲染时不会调用该函数
(4) 使用场景:
一般情况下,react的父级组件的render函数重新渲染会引起子组件的render方法的重新渲染。
但有时子组件接收父组件的数据后没有变动。如果子组件此时不需要变动还是执行了render方法,
跟着父组件重新渲染了,这就会影响程序性能,而这时就可以使用shouldComponentUpdate来解决这个问题。
//父级
//当在父级组件中调用setState方法时,它的子组件Book都会重新执行render方法去再次渲染
export default class App extends Component {
constructor(props) {
super();
this.state = {
kind: '分类',
};
}
componentDidMount() {
this.setState({
kind: '新的分类',
});
}
render() {
return (
<div>
<Book />
<div>{this.state.kind}</div>
</div>
);
}
}
// 子级
// 当子组件Book中的state并未作任何改变,所以不需要再随父级而重新渲染
// 添加shouldComponentUpdate()函数,相当于先做判断,如果返回true,再次执行render方法去重新渲染,否则不再重新渲染。
export class Book extends Component {
constructor(props) {
super();
this.state = {
name: 'React前端框架'
}
}
shouldComponentUpdate(nextProps, nextState) {
console.log(nextProps);
console.log(nextState);
return nextState.name !== this.state.name;
}
render() {
return (
<div>
{this.state.name}
</div>
);
}
}
getSnapshotBeforeUpdate(prevProps, prevState)
(1) 该方法在 render() 之后,但是在输出到 DOM 之前执行,用来获取渲染之前的快照。
(2) 当我们想在当前一次更新前获取上次的 DOM 状态,可以在这里进行处理,该函数的返回值将作为参数传递给下个生命周期函数
(3) 必须return一个结果或者返回null
getSnapshotBeforeUpdate()在DOM更新之前,将立即调用新的生命周期方法。此方法的返回值将作为第三个参数传递给componentDidUpdate()。
componentDidUpdate()
该函数会在 DOM 更新后立即调用,首次渲染不会调用该方法。我们可以在这个函数中对渲染后的 DOM 进行操作
export default class App extends Component {
constructor(props) {
super(props);
// 使用此方法来创建ref。将其赋值给一个变量,通过ref挂载在dom节点或组件上,
// 该ref的current属性将能拿到dom节点或组件的实例
this.listRef = React.createRef();
}
getSnapshotBeforeUpdate(prevProps, prevState) {
// 捕获滚动的位置,以便后面进行滚动 注意返回的值
if (prevProps.list.length < this.props.list.length) {
let list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// 如果有 snapshot 会进行滚动的调整,这样子就不会立即将之前的内容直接弹上去
if (snapshot !== null) {
let list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
render() {
return (
<div ref={this.listRef} style={{
height: '150px', width: '300px', backgroundColor: 'lightblue', overflow: 'auto' }}>
jhvdjhhsdjvdlsd;f'asjdfklsadfljksdkv,vkljiuerygdokosdgjsodjgsd
dkgosdjgojsdlgksdgesrg;sdkfsod;klvkdsovsedkborkrtlfgkbfdposdksd;psdlsdfd
dfkdsjkjlkspgoiuohpksdlvjouhs;vl2q373r7734989490i20i3tu794554'4tu45u
jkdgsldk;kou45oerkhldfhikrth;;erlhldkfghue5ir0yi56ut;jtjpt
eougieudlvz,hahd7236879405496--ul;,bdljvkdisuf98fw347t9rltr;
eig9ut0iephekdfmljsdd';slt9058307582739ruweosglsdgoeur9tgrt78356877487
skjsdg,md,jsdjvljsdogjlsdklg sdvhdkgkdllgdslkgkdljgldvjgjdfjlgdf
dkbhdkgkvdshjhsdkhgsjdkfglsdfieysdffuisdfjvjdsjvhjhsdkjjvkjxdjgserd
</div>
);
}
}
3、卸载阶段
componentWillUnmount()
(1) 该方法会在组件卸载及销毁前调用,
(2) 在每一个组件执行render方法前调用
(3) 在浏览器端和服务器端都可以调用
我们可以在这里做一些清理工作,如:组件内的定时器、未完成的请求等
官网中时钟示例
组件内先执行constructor,然后执行render,将虚拟dom插入到组件中,并渲染初始状态到页面中;
然后执行挂载钩子函数,启动计时器,每隔一秒执行一次自定义的timeFn方法,组件会调用setState方法去重新渲染页面;
最后将计时器从组件中清除,此时调用componentWillUnmount() 卸载生命周期函数,终止计时器。
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
date: new Date()
};
}
//挂载钩子函数
componentDidMount() {
this.timerID = setInterval(
() => this.timeFn(),1000
);
}
//卸载钩子函数
componentWillUnmount() {
clearInterval(this.timerID);
}
//时间处理方法
timeFn() {
this.setState({
date: new Date()
});
}
// 渲染
render() {
return (
<div>
<h2>当前时间:{this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
4、错误处理
当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法
(1) static getDerivedStateFromError()
(2) componentDidCatch(error,info)
import React,{Component} from 'react';
export default class App extends Component {
state = {
hasError:true
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error,info) {
this.setState({hasError:true});
console.log(info);
}
render(){
if(this.state.hasError){
return (<div>报错了</div>);
}
return this.props;
}
}
受控组件与非受控组件
受控组件
:类似vue的双向数据绑定,数据和视图之间可以相互影响
使用input输入框,如果需要取值并且需要修改它时,需要使用value结合onChange事件。
非受控组件
:类似单向数据流,只可以使用数据改变视图
使用input输入框时,如果只需要取值时,使用defaultValue就可以。
import React, { Component } from 'react';
export default class App extends Component {
state = {
info: '填写内容'
}
render() {
let { info } = this.state;
return (
<div>
{/* 无论是单、双标签,结尾都需要闭合 */}
{/*
*/}
{/* 如果input输入框不写onChange事件,那么输入框的值只是可读的,不能修改 */}
<input type="text"
value={info}
onChange={({ target }) => {
this.setState({
info: target.value
})
}
}/>
<button onClick={
() => {
if (info === null || info === "") {
alert("输入框不能为空");
}
console.log(info);
}}>
点击
</button>
</div>
)
}
}