写在前边
这是一片记录react生命周期的文章。博主最近在重读react官网,想对react有更深度的理解。如果你同样对react这个优秀的框架抱有兴趣,欢迎联系我一同探讨!!文中有描述含混不清和错误的地方,望不吝赐教,直接指出
文末附有验证生命周期API先后顺序的demo
QQ: 272473132
github: https://github.com/CregskiN
1. 基础概念
Mount、Update、Unmount
- Mounting:组件初次渲染
- Updating:因某些原因,组件需要更新
- Unmounting:组件卸载。即他的父组件的render()函数中不再return他
渲染阶段、预提交阶段、提交阶段
- Render phase:react负责将JSX渲染为DOM对象(仅渲染,不更新页面)
一旦state或props改变,Render phase阶段将被强制重新执行(具体次数无法预计)。所以尽量不要Render phase更新state或props。
- Pre-commit phase:react渲染完DOM,预提交阶段,在这里,可以读取DOM信息
- Commit phase:react渲染并完成DOM更新
官方建议:仅在这个阶段执行副作用、state更新
2. Mounting阶段
Render phase
constructor()
初始化state、props
- 初始化state不要用props.color,可以直接用this.props.color
- 为事件处理 绑定this
static getDerivedStateFromProps(props, state)
用处罕见:派生状态组件,即state在任何时候都取决于prop
不建议使用派生组件,会导致受控于非受控含混不清,state混乱
- return newState 返回对象以更新state,若返回null,不更新任何内容(无法使用this.state修改state)
render()
当
render
被调用时,它会检查this.props
和this.state
的变化并返回以下类型之一:- React 元素。通常通过 JSX 创建。例如,
会被 React 渲染为 DOM 节点,
会被 React 渲染为自定义组件,无论是还是
均为 React 元素。 - 数组或 fragments。 使得 render 方法可以返回多个元素。欲了解更多详细信息,请参阅 fragments 文档。
- Portals。可以渲染子节点到不同的 DOM 子树中。欲了解更多详细信息,请参阅有关 portals 的文档
- 字符串或数值类型。它们在 DOM 中会被渲染为文本节点
- 布尔类型或
null
。什么都不渲染。(主要用于支持返回test &&
的模式,其中 test 为布尔类型。)
- React 元素。通常通过 JSX 创建。例如,
Pre-commit phase
Commit phase
componentDidMount()
- this.setState() 触发额外渲染,但会在浏览器更新屏幕之前
- 副作用(side effect)
3. Updating阶段
当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
Render phase
static getDerivedStateFromProps()
shouldComponentUpdate(nextProps, nextState)
- return false ? 不调用 Updating阶段的render() : 调用 Updating阶段的render();
render()
Pre-commit phase
getSnapshotBeforeUpdate(prevProps, prevState)
在以下场景调用:
- 想要在组件改变之前获取DOM信息,如滚动位置
博主疑问:为什么只有Updating阶段有getSnapshotBeforeUpdate而Mounting阶段没有?
是因为在Mounting阶段读取DOM的需求吗?
还是说官方建议Mounting阶段读取DOM的逻辑放到componentDidMount执行?
Commit phase
componentDidUpdate(prevProps, prevState, snapshot)
this.setState() 额外渲染,影响性能!! 而且一定用if包裹,否则会死循环
componentDidUpdate(prevProps) { // 典型用法(不要忘记比较 props): if (this.props.userID !== prevProps.userID) { this.fetchData(this.props.userID); }}
4. Unmounting阶段
当组件从 DOM 中移除时会调用如下方法:
Render phase
Pre-commit phase
Commit phase
componentWillUnmount()
5. other lifecycle
错误处理
当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:
Render phase
static getDerivedStateFromError(error)
- return newState;
Commit phase
componentDidCatch(error, info)
- error:Error实例
- info:React.ErrorInfo 包含错误栈信息
父组件Lifecycle.tsx
import React, { Component } from 'react';import CanUnmount from './CanUnmount';interface LifecycleProps { };interface LifecycleState { counter: number;};function tableLog(logName: string, obj: any) { if (obj !== null && obj !== undefined) { if (typeof obj === 'object' && obj.length) { console.log(logName); console.table(obj); } }}class lifecycle extends Component<LifecycleProps, LifecycleState> { handleAdd() { this.setState((state, props) => ({ counter: state.counter + 1 })) } handleLess() { this.setState((state, props) => ({ counter: state.counter - 1 })) } handleReset() { this.setState((state, props) => ({ counter: 0 })) } // Mounting constructor(props: LifecycleState) { super(props); this.state = { counter: 0, }; console.group({ lifecycle: 'constructor' }); console.table(this.state) console.groupEnd(); this.handleAdd = this.handleAdd.bind(this); this.handleLess = this.handleLess.bind(this); this.handleReset = this.handleReset.bind(this); } static getDerivedStateFromProps(props: LifecycleProps, state: LifecycleState) { // render-phase 获得来自副组件的props派生 console.group({ lifecycle: 'getDerivedStateFromProps' }); tableLog('props', props); tableLog('state', state); console.groupEnd(); return null; } render() { console.group({ lifecycle: 'render' }); console.groupEnd(); const { counter } = this.state; const { handleAdd, handleLess, handleReset } = this; if (counter < 3) { return ( <div> {counter} <button onClick={handleAdd}>+1</button> <button onClick={handleLess}>-1</button> </div> ) } else { return ( <div> {counter} <button onClick={handleReset}>reset</button> <CanUnmount /> </div> ) } } componentDidMount() { console.group({ lifecycle: 'componentDidMount' }); console.log('Mounting finished') console.groupEnd(); console.log('\n'); } // Updating // static getDerivedStateFromProps shouldComponentUpdate(nextProps: LifecycleProps, nextState: LifecycleState) { console.group({ lifecycle: 'shouldComponentUpdate' }); tableLog('nextProps', nextProps); tableLog('nextState', nextState); console.groupEnd(); if (this.state.counter !== nextState.counter) { return true; } return false; } // render getSnapshotBeforeUpdate(prevProps: LifecycleProps, prevState: LifecycleState) { console.group({ lifecycle: 'getSnapshotBeforeUpdate' }); tableLog('prevProps', prevProps); tableLog('prevState', prevState); console.log('getSnapshotBeforeUpdate finished'); console.groupEnd(); return { snapshot: 'ss' }; } componentDidUpdate(prevProps: LifecycleProps, prevState: LifecycleState, snapshot: any) { console.group({ lifecycle: 'componentDidUpdate' }); tableLog('prevProps', prevProps); tableLog('prevState', prevState); tableLog('snapshot', snapshot); console.log('Update finished'); console.groupEnd(); console.log('\n'); } // Upmounting componentWillUnmount() { console.group({ lifecycle: 'componentWillUnmount' }); console.log('Unmounting finished'); console.groupEnd(); console.log('\n'); }}export default lifecycle;
子组件 CanUnmoun.tsx
import React, { Component } from 'react';interface CanUnmounProps { };class CanUnmoun extends Component { componentWillUnmount() { console.group({ lifecycle: 'CanUnmount componentWillUnmount' }); console.log('CanUnmount Unmounting finished'); console.groupEnd(); console.log('\n'); } render() { return ( <div> Component CanUnmoun </div> ) }}export default CanUnmoun;
Reference
React.Component
快速了解React的新功能Suspense和Hooks