文章目录
前言
按照我的学习理念,学新不学旧,尽量减少心智负担。所以旧的生命周期我就不记录了。
整体和vue也非常像。
生命周期整体图
先上完整生命周期图:
(图片来源b站尚硅谷免费课程)
废弃了旧版本的三个钩子函数componentWillReceiveProps()
、componentWillMount()
、componentWillUpdate()
,可不记。
以下分为几个阶段讲解
挂载阶段
constructor
react组件在挂载阶段,先会执行类定义里的constructor
构造器(这也是类的特性),初始化阶段:
//构造器
constructor(props) {
super(props);
//可以做些事,例如初始化状态
this.state = { count: 0 };
}
getDerivedStateFromProps
接着进入getDerivedStateFromProps
钩子
功能:若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps()
。官方说尽量少用或不用,容易导致代码冗余。
static getDerivedStateFromProps(props, state) { // 需要加静态修饰符
console.log("getDerivedStateFromProps", props, state);
return null; // 必须返回一个null或者state对象,返回的state对象会覆盖state原值,如果直接返回props,那么state的值在任何时候都取决于props,所以叫state派生于prop
}
个人感觉可以放到render中处理,因为更新阶段也会执行render,这样这个钩子就可以不用记了哈哈。
render
然后进入render
钩子,渲染阶段:
// 写jsx渲染html
render() {
const { count } = this.state;
return (
<div>
{count}
</div>
);
}
componentDidMount
接着进入componentDidMount
钩子,组件已挂载阶段:
componentDidMount() {
// 常用,一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息。
}
更新阶段
当props或者state更新后,就会进入组件的更新阶段
getDerivedStateFromProps
这个先
shouldComponentUpdate
先进入shouldComponentUpdate()
钩子,控制组件更新的“阀门”:
shouldComponentUpdate() {
// 不写这个钩子的话,底层默认返回true,自己改写的话必须返回布尔值
return true; // 如果为false生命周期就不会走下去,被截胡在这
}
这个钩子我有专门讲过,文章。
render
然后进入render()
钩子
getSnapshotBeforeUpdate
在即将更新视图的时候,像快门一样咔擦停住:
getSnapshotBeforeUpdate() {
return xxx // 这里可以返回值,在componentDidUpdate可以获取到,例如componentDidUpdate()可以拿到更新前的数据
}
componentDidUpdate(preProps, preState, snapshotValue) {
// 拿到更新前的props,state,getSnapshotBeforeUpdate的快照值
console.log(
"Count---componentDidUpdate",
preProps,
preState,
snapshotValue
);
}
可以用于获取前后更新的dom的某些状态,例如高度、滚动高度等等,看需求来要。
componentDidUpdate
再进入componentDidUpdate()钩子,组件更新完成:
componentDidUpdate() {}
强制更新
通过调用this.forceUpdate()
,不更改任何数据,强制更新,进入生命周期流程,结合上文加看图。
卸载阶段
componentWillUnmount
当组件将要卸载,会进入componentWillUnmount
钩子函数,一般会用于清除一些定时器,实例,取消订阅消息等。例如:
componentWillUnmount() {
//清除定时器
clearInterval(this.timer); // 防止定时器叠加
}
可以通过ReactDOM.unmountComponentAtNode(document.getElementById(""))
来强制卸载组件。
错误捕获
getDerivedStateFromError与componentDidCatch
当有一个子组件出现js报错问题,父组件会在页面上直接渲染不出来,这在生产环境上会给用户带来不好的体验。
react提供了错误处理相关的钩子getDerivedStateFromError
与componentDidCatch
,能够让项目在生产环境中,不会因为一个子组件的报错而影响父级组件的显示(开发环境还是会报错,但是可以×掉报错页面),该报错组件显示一个我们设定好的界面,可称为错误边界。
// 在父组件中
state = {hasError: false}
static getDerivedStateFromError(error) { // 捕获生命周期和render调用时,发生的错误
console.log(error); // 在render之前触发
// 返回新的state
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// 统计页面的错误。发送请求发送到后台去,为了日后修改bug
console.log(error, info); // error —— 抛出的错误,info —— 带有 componentStack key 的对象,其中包含有关组件引发错误的栈信息
}
// 然后在渲染的时候,如果子组件出错,就用个标签替换一下
render() {
return (
<div>
{this.state.hasError ? <h1>网络出错了</h2> : <Child />}
</div>
)
}
简单封装
一般都会封装成一个公共组件src/components/ErrorBoundary/index.js
import React, { Component } from 'react';
export default class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = {
flag: false
};
}
static getDerivedStateFromError(error) {
console.log(error)
return {
flag: true
}
}
/* error: 抛出的错误
* info: 带有 componentStack key 的对象,其中包含有关组件引发错误的栈信息
*/
componentDidCatch(error, info) {
}
render() {
return (
<div>
{this.state.flag ? <h1>发生错误,请稍后再试!</h1> : this.props.children}
</div>
)
}
}
然后一般在layout组件中使用(因为可以覆盖到页面内部的所有子组件):
import ErrorBoundary from '@/components/ErrorBoundary';
// 在模板中包裹起来即可
<ErrorBoundary>
<Zujian>
</ErrorBoundary>
不过我后面试了一下,如果放在layout组件中,当报错了出现错误边界后再切换到其他页面错误边界仍然显示,所以建议还是放在页面组件里吧。
componentDidCatch的注意点
没实践过,大概稍微记录下,可能有错误
- 开发环境下,错误会冒泡会到
window.onerror
或window.addEventListener('error', callback)
- 生产环境下,错误不会冒泡,只被
componentDidCatch
拦截