目录
扩展学习资料
React New Component Lifecycle【新生命周期】
React 组件新生命周期详解
React组件老生命周期 v15.x
为什么Fiber Reconciler要有新的生命周期函数呢?
新的组件生命周期
getDerivedStateFromProps
挂载阶段
更新阶段
卸载阶段
异常捕获
新版组件升级
升级组件版本
APP.js
component.js
升级组件遍布-保留选项
扩展学习资料
名称 | 链接 |
React 组件 | React.Component – React |
React v16.3 组件生命周期 | 关于React v16.3 新生命周期 - 掘金 |
React 组件图 | React lifecycle methods diagram |
React New Component Lifecycle【新生命周期】
掌握新的组件生命周期,在老的生命周期组件中进行更新代码。
React 组件新生命周期详解
React组件老生命周期 v15.x
- Initialization【初始化】:state的初始化;及props是否转化为state;setup;
- Mounting【挂载过程】:componentWillMount()【新版删除1】->render()->componentDidMount()
- Updation【业务更新】:(props【父级传递的props更新;被动更新】)=>componentWillReceiveProps()【新版删除2】->shouldcomponentUpdate(){return true}-> componentWillUpdate()【新版删除3】->render()->componentDidUpdate();(states【主动调用setState更新】)=>shouldcomponentUpdate(){return true}-> componentWillUpdate()【新版删除3】->render()->componentDidUpdate()
- Unmounting【卸载过程】:componentWillUnmount
为什么Fiber Reconciler要有新的生命周期函数呢?
答:1.Fiber Reconciler中为了在没有更新前再次混入更新任务导致Fiber更新整个流程有点混乱;为了适配Fiber Reconciler就取消了在更新前可以再次触发更新的操作。
(有部分同学会在componentWillMount中setState;异步请求)
Fiber Reconciler要求对组件的生命周期,至少在更新前做到要纯函数行为。【不能写入异步的操作,影响更新流程】
2.我们之前并不是Fiber Reconciler中导致的,在刚开始接触React的时候会有大部分同学,会在componentWillMount进行set相关操作(setState,进行初始化redux操作,以及调用接口异步操作)【应该写在构造函数constructor中】,不然会多进行一遍vDom diff;
另一个就是componentWillUpdate中进行setState操作【不带任何条件的操作】,相当于触发了一个无线循环。
总结:基于这两点,一个是我们在老的生命周期中对一些方法的滥用,以及为了适配Fiber Reconciler当中更新前的一些流程处理,是为了我们后面的异步渲染做准备,所以React16.x之后就更新了新的生命周期函数。
新的组件生命周期
React v16.3版本
static getDerivedStateFromProps(props, state)【替换componentWillReceiveProps】、static getSnapshotBeforeUpdate【静态方法,静态方法中不能调用this】
1.挂载时:constructor()【构造函数】->getDerivedStateFromProps()【新增1】->render()->React更新DOM和refs->componentDidMount()
2.更新时:New Props触发更新 =>getDerivedStateFromProps()【新增1】->shouldComponentUpdate(){return true}->render()->getSnapshotBeforeUpdate()【新增2】->React更新DOM和refs->componentDidUpdate()
setState()触发更新=>shouldComponentUpdate(){return true}->render()->getSnapshotBeforeUpdate()【新增2】->React更新DOM和refs->componentDidUpdate()
forceUpdate()触发更新=>render()->getSnapshotBeforeUpdate()【新增2】->React更新DOM和refs->componentDidUpdate()
3.卸载时:componentWillUnmount()
“Rebder(更新)阶段【render()方法之前】”:纯净【纯函数】且不包含副作用【重新触发setState、异步请求、redux更新】。可能会被React暂停,中止或重新启动。
“Pre-commit阶段”:可以读取DOM
“Commit阶段”:可以使用DOM,运行副作用,安排更新。
React v16.4版本【通常用的v16.4版本】
与v13比
挂载阶段【constructor】和更新阶段【New Props,setState(),forceUpdate()】之后都会触发getDerivedStateFromProps()
getDerivedStateFromProps
是一个派生state,所有可能触发state变更的都会经过这个静态方法,主要是返回一个派生类的state
挂载阶段
挂载阶段的函数
- constructor 构造函数,初始化state,以及为事件处理函数绑定实例。【初始化数据】
- getDerivedStateFromProps (nextProps, state) { this === window } 新增的静态方法。返回一个新的state,或者是null(后续不更新)。
- render 渲染函数。
- componentDidMount 挂载成功后立即调用的函数。【副作用操作,调用fetch(接口数据),绑定真实dom数据,如window.onresize,window.onscroll事件等】
更新阶段
更新阶段的函数
- getDerivedStateFromProps props变化或者state方法触发。
- shouldComponentUpdate 判断是否进行更新。【可以对UI更新逻辑进行优化】
- render 渲染函数。
- getSnapshotBeforeUpdate render方法之后调用,返回一个dom更改之前的快照,将配合后续的componentDidUpdate方法使用。
- componentDidUpdate(oldprops, oldstate) 更新后会被立即调用。如果设置了getSnapshotBeforeUpdate(),会多出一个componentDidUpdate(oldprops, oldstate,snapshot);异步,进行setState操作,要进行条件判断,避免死循环更新,
卸载阶段
卸载阶段的函数
- componentWillUnmount 卸载函数,组件卸载及销毁之前直接调用。主要用于清除一些在组件生命周期订阅,真实DOM事件以及setTimeout/setInterval的返回值。【不做的话,可能会引起内存泄漏、溢出】
异常捕获
v16.x 异常捕获的函数【全局错误处理,后代组件异常处理】
- componentDidCatch(error,info) 生命周期方法在后代组件抛出错误后被调用。方法接收两个参数(error,info),分别是错误信息和错误组件的栈信息。;做一些错误日志上传,错误信息,错误源。不推荐在这里修改state,处理UI页面展示。避免setState影响Fiber 协调
- getDerivedStateFromError(error) 在后代组件抛出错误后调用,接收一个参数(error)表示具体错误信息。做一些兼容错误页面,返回一些对象,控制页面元素,用户体验更好,是否出现不可抗性错误。新增的静态类型,返回一个派生类的state,建议在这里修改state进行页面处理
v15.x之前的处理方案:
1.关键代码加try{}catch(e){}包裹
2.监听window.onerror方案。也可以通过window.addEventListener("error", function(evt){})
3.react 提供的一个内置捕获错误方法:unstable_handleError;没有正式对外宣布过,并不是一个标准的稳定的方法
新版组件升级
升级组件版本
- componentWillMount
- render方法之前调用,在此调用setState并不会触发再次渲染。
- 通常会在这个方法中进行页面标题的一些修改以及其他与再次render不相关的操作。
- UNSAFE_componentWillMount(不推荐用这个替换componentWillMount)
- 与state相关的操作挪到constructor方法中执行。
- 异步操作挪到componentDidMount中执行。
- componentWillUpdate
- 在组件收到新的props或者state时,会在渲染之前调用。
- 方法内不能调用setState,触发循环,内存泄漏。
- UNSAFE_componentWillUpdate(官方保留方法,不推荐使用)
- 应该在shouldComponentUpdate中判断是否更新。
- componentWillReceiveProps
- 接收父级组件传递过来最新的props,转化为组件内的state。
- 判断是否进行更新或者执行异步请求数据。
- UNSAFE_componentWillReceiveProps(官方保留方法,不推荐使用)
- 与渲染相关的props直接渲染,不需要处理为组件内state。
- 异步数据请求在componentDidUpdate中处理。
- getDerivedStateFromProps方法替换,需要考虑生命周期的执行顺序。【挂载阶段【constructor】和更新阶段【New Props,setState(),forceUpdate()】之后都会触发getDerivedStateFromProps()】
APP.js
import React, { Component } from "react";
import NewCycle from "./component";
import "./styles.css";
const mockList = [{key: 1,text: "这是列表项-1"},{key: 2,text: "这是列表项-2"},{key: 3,text: "这是列表项-3"},{key: 4,text: "这是列表项-4"}
];
export default class App extends Component {// let cycleList = [];state = {cycleList: []};// const [cycleList, setCycleList] = useState([]);queryList = () => {this.setState({cycleList: mockList});};render() {return (<div className="App"><NewCyclecycleList={this.state.cycleList}queryListCB={this.queryList}/></div>);}
}
component.js
import React, { Component } from "react";
import shallowequal from "shallowequal";// 开源第三方库,shallowequal比较值相等,或者对象含有相同的属性、且属性值相等
export default class NewCycle extends Component {constructor(props) {super(props);this.state = {list: [],parentList: []};}/* UNSAFE_componentWillReceiveProps(nextProps) {if (!shallowequal(this.props, nextProps)) {const { cycleList } = nextProps;this.setState({list: cycleList.map(item => ({...item,label: "origin" // 组件内部要调用一些逻辑}))});}} componentDidUpdate(prevProps, prevState) {// console.log(prevProps, prevState, this.props, this.state);// 比getDerivedStateFromProps晚一些,更新完成之后才触发if (!shallowequal(this.props, prevProps)) {const { cycleList } = this.props;this.setState({list: cycleList.map((item) => ({...item,label: "标题"}))});}}*/static getDerivedStateFromProps(props, state) {// newprops,oldstate// 触发时机有4种,要做好兼容判断const { cycleList } = props;const { parentList } = state;/*** prueComponent 在这里如果返回有数据的对象,* shouldComponentUpdate 会返回true* 最好是不要再用state来控制*/// 初始化不执行 &&if (cycleList.length > 0 && !shallowequal(cycleList, parentList)) {return {list: cycleList.map((item) => ({...item,label: "标题"})),parentList: cycleList};}return null;}queryList = () => {const { queryListCB } = this.props;if (typeof queryListCB === "function") {queryListCB();}};render() {const { list } = this.state;return (<div>{list.map(item => (<div key={item.key}>{item.label}-{item.text}</div>))}<button onClick={this.queryList}>按钮</button></div>);}
}
升级组件遍布-保留选项
老工程代码量很多,改动特别麻烦。需要回归的点非常多!怎么解决?
npx react-codemod rename-unsafe-lifecycles
// 此方法会给需要添加'UNSAFE_'的方法加上它。 此时版本为【v16.x】 (v15.x < v16.x < v17)