react组件卸载调用的方法_react之生命周期API的总结

3e14839940ca4f299d14e3780f31b293.png

3558d7c71c694082f5b1fc3d3215e6dc.png

写在前边

这是一片记录react生命周期的文章。博主最近在重读react官网,想对react有更深度的理解。如果你同样对react这个优秀的框架抱有兴趣,欢迎联系我一同探讨!!文中有描述含混不清和错误的地方,望不吝赐教,直接指出

文末附有验证生命周期API先后顺序的demo

QQ: 272473132
github: https:// github.com/CregskiN

知乎文章的代码编写体验是在不敢恭维,如果实在影响阅读,请移步

关于react生命周期的总结 - 个人文章 - SegmentFault 思否​segmentfault.com
81687cadece0602ca501fce42b857e90.png

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更新
官方建议:仅在Commit phase执行副作用、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.propsthis.state 的变化并返回以下类型之一:
  • React 元素。通常通过 JSX 创建。例如,会被 React 渲染为 DOM 节点, 会被 React 渲染为自定义组件,无论是 还是 均为 React 元素。
  • 数组或 fragments。 使得 render 方法可以返回多个元素。欲了解更多详细信息,请参阅 fragments 文档。
  • Portals。可以渲染子节点到不同的 DOM 子树中。欲了解更多详细信息,请参阅有关 portals 的文档
  • 字符串或数值类型。它们在 DOM 中会被渲染为文本节点
  • 布尔类型或 null。什么都不渲染。(主要用于支持返回 test && 的模式,其中 test 为布尔类型。)

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 包含错误栈信息

关于getDerivedStateFromError和componentDidCatch的用法,请见我另一篇文章:

CregskiN:react之错误边界(Error Boundaries)​zhuanlan.zhihu.com
1150be35423efb7686f07b5bb4d41ea8.png

父组件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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值