【React】生命周期详解

文章目录

    • 一、生命周期钩子函数
      • 1.1、旧版钩子函数【淘汰】
      • 1.2、新版钩子函数【新增】
      • 1.3、常用的钩子函数
    • 二、生命周期各阶段
      • 2.1、挂载阶段
        • render【!】
        • constructor【!】
        • componentDidMount【!】
        • static getDerivedStateFromProps【!】
      • 2.2、更新阶段
        • componentDidUpdate【!】
        • shouldComponentUpdate
        • getSnapshotBeforeUpdate
      • 2.3、卸载阶段
        • componentWillUnmount【!】
      • 2.4、错误处理
        • componentDidCatch
        • static getDerivedStateFromError

一、生命周期钩子函数

  • React 的生命周期从广义上分为三个阶段:挂载、渲染、卸载
  • 因此可以把 React 的生命周期分为两类:挂载卸载过程和更新过程

1.1、旧版钩子函数【淘汰】

  • 了解即可,因为现在一般都是用新版的,但是有些公司以前的项目是用旧版本写的,这时你就需要知道里面一些钩子函数是做什么用的
  • 16.3之前的版本生命周期图示:

【React】生命周期详解_第1张图片

  • 旧版本独有的,17.0版本已经删除了的钩子函数(17.0之前的版本仍可以继续使用):
  • componentWillMountcomponentWillReceivePropscomponentWillUpdate
  • 在使用这几个钩子函数时,前面要加UNSAFE_

import React ,{Component} from 'react'
import ReactDOM  from 'react-dom'
// 子组件
class Child extends Component{
    // 初始化数据
    constructor(props){
        super(props);
        this.state = {};
    }
    // 当组件接收到新的props值会触发,新版已经淘汰
    UNSAFE_componentWillReceiveProps(){
        console.log("componentWillReceiveProps 运行了");
    }
    render(){
        return(<div>子节点:{this.props.num}</div>)
    }

}
class App extends Component {
    constructor(props) {
        console.log('constructor 运行了');
        super(props);
        this.state = {num:0};
    }
    // 组件挂载前,新版已经淘汰了
    UNSAFE_componentWillMount(){
        console.log('componentWillMount 运行了')
    }
    // 箭头函数
    changeNum = ()=>{
        this.setState({num:this.state.num+1})
    }
    // 渲染数据
    render() {
        console.log('render 运行了');
        return (<div>
            根节点<br/>
            {/* 
必须加/
*/
} <button onClick={this.changeNum}>++</button>{this.state.num} <br/>子组件: <Child num={this.state.num}/> </div>); } // 组件更新前,新版已经淘汰了 UNSAFE_componentWillUpdate(){ console.log('componentWillUpdate 运行了'); } } ReactDOM.render( <App />, document.getElementById('root') )

【React】生命周期详解_第2张图片
【React】生命周期详解_第3张图片

1.2、新版钩子函数【新增】

  • 16.3版本生命周期图示:
    【React】生命周期详解_第4张图片

  • 16.4及以上版本生命周期图示:
    【React】生命周期详解_第5张图片

  • 增加了:static getDerivedStateFromProps()getSnapshotBeforeUpdate()

  • 用来替代被弃用的旧版本那三个钩子函数

  • 还增加了:static getDerivedStateFromError()componentDidCatch()

  • 用来处理错误


import React ,{Component } from 'react'
import ReactDOM  from 'react-dom'
class App extends Component {
    constructor(props) {
        console.log('constructor 运行了');
        super(props);
        this.state = {
            count: 0
        };
    }
    // 一般用不到:通过props设置获取到 state的值  
    // getDerivedStateFromProps前面要加上static保留字,声明为静态方法,不然会被react忽略掉
    // getDerivedStateFromProps里面的this为undefined
    static getDerivedStateFromProps(nextProps, prevState) {
        console.log("getDerivedStateFromProps 运行了");
        return true;
    }
    // 一般用不到:滚动的特殊处理,与componentDidUpdate 配对使用的
    /*  在react `render()`后的输出被渲染到DOM之前被调用。
        它使您的组件能够在它们被潜在更改之前捕获当前值(如滚动位置)。
        这个生命周期返回的任何值都将作为参数传递给componentDidUpdate()*/ 
    getSnapshotBeforeUpdate(prevProps, prevState) {
        console.log("getSnapshotBeforeUpdate 运行了");
        return 11;
    }
    componentDidUpdate(prevProps, prevState, snapState) {
        console.log("componentDidUpdate 运行了", snapState);
    }
    // 本身或后代有报错会触发
    componentDidCatch(error, info) {
        console.log("componentDidCatch 运行了");

    }
    // 后代组件抛出错误后会触发
    static getDerivedStateFromError(nextProps, prevState) {
        console.log("getDerivedStateFromError 运行了");

    }
    // 定义事件
    changeCount = () => {
        this.setState({ count: this.state.count + 1 })
    }
    // 渲染数据
    render(){
        const { count} = this.state;
        return(
            <div>
                <button onClick={this.changeCount}>++ {count}</button>
            </div>
        )
    }
}
ReactDOM.render(
 <App />,
  document.getElementById('root')
)

【React】生命周期详解_第6张图片
【React】生命周期详解_第7张图片

1.3、常用的钩子函数

  • 下面的钩子函数在新老版本都是比较常用的
import React, { Component } from 'react';
class App extends Component {
    //初始化  ,state  ,props,ref,绑定this
    constructor(props) {
        super(props);
        this.state = {}
    }
    //渲染  ,父组件 执行了渲染,默认子组件的渲染函数也要执行
    render() {
        return (<div>首页</div>);
    }
    
    //是否需要更新  ,不需要 返回 false   ;需要 返回true
    shouldComponentUpdate() {
        return true;
    }
    
    // DOM操作,实例化,数据请求
    //异步处理,有副作用的处理  ;获取数据、执行 setTimeout  setInterval
    componentDidMount() {
		console.log('componentDidMount')
    }
    
    //更新时的钩子函数
    componentDidUpdate() {
		console.log('componentDidUpdate')
    }
    
    //卸载 回收操作  定时器  解除绑定
    componentWillUnmount() {
		// 类似于vue 的beforeDestory
    	console.log('componentDidUpdate')
    }
}

export default App;

  • componentDidMount 案例:实现文字透明度的渐变效果
import React ,{Component} from 'react'
import ReactDOM  from 'react-dom'
class App extends Component{
  constructor() {
    super();
    this.state = {
        opacity: 1  // 不透明
    }
  }
  //在这个钩子函数中可以获取数据
  componentDidMount(){
    setInterval(()=>{
    	//报错,this指向不对,目前指向了window,可以使用箭头函数
        let myOpacity = this.state.opacity; 
        myOpacity -= 0.05;
        //如果透明度到0,重新置1
        if(myOpacity<=0){
            myOpacity = 1;
        }
        this.setState({
            opacity:myOpacity
        })
    },500);
  }
  render() {

    return (<>
        <h1 style={{"opacity": this.state.opacity}}>hello world</h1>
    </>);
  }
}
ReactDOM.render(
 <App />,
  document.getElementById('root')
)

【React】生命周期详解_第8张图片

二、生命周期各阶段

React中组件也有生命周期,也就是说也有很多钩子函数供我们使用, 组件的生命周期,我们会分为四个阶段,初始化(挂载)、运行中(更新)、销毁(卸载)、错误处理(异常)(16.3之后)

常用的生命周期方法就下面几个,为啥?因为好记,好记才会常用。程序员的本性。
其实其他生命周期方法也好用,但是需要特地条件下使用才会觉得方便。所以就不太常用

  • constructor()
  • render()
  • componentDidMount()
  • componentDidUpdate()
  • componentWillUnmount()
  • getDerivedStateFromProps

2.1、挂载阶段

  • 当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:
  1. constructor
  2. static getDerivedStateFromProps
  3. render
  4. componentDidMount
render【!】
  • render()
  • 调用:在组件挂载之前
  • 是 class 组件中唯一必须实现的方法

使用方法

  • 当他被调用时,他将计算this.propsthis.state,并返回以下一种类型:
返回的类型 描述
React元素 通常通过 JSX 创建,比如DOM元素或组件
数组 或 fragments 使得 render 方法可以返回多个元素。
Portals 可以渲染子节点到不同的 DOM 子树中
字符串或数值 它们在 DOM 中会被渲染为文本节点
null 或 布尔值 什么都不渲染。

使用限制

  • render()方法必须是一个纯函数
  • 他不能改变state
  • 也不能直接和浏览器进行交互
  • 应该将事件放在其他生命周期函数中

注意:

  • 如果shouldComponentUpdate()返回false
  • 则不会调用 render()。
constructor【!】
  • constructor(props)
  • 调用:在组件挂载之前
constructor(props) {
  super(props);
  this.state = {
    isLiked: props.isLiked
  };
}

constructor使用条件

  • 在添加其他内容前
  • 调用super(props)
  • 将父组件传来的props绑定到这个类中
  • 不然this.props将会无法得到

constructor作用

  • 通过给 this.state 赋值对象来初始化内部 state
  • 为事件处理函数绑定实例

constructor()在以下条件可以不用:

  • 不初始化 state
  • 不进行方法绑定
  • 组件内全是纯函数写法。

constructor()中不要这样做:

  • 调用 setState() 方法
  • 引入任何副作用或订阅
componentDidMount【!】
  • componentDidMount()
  • 调用:组件挂载到DOM后,只会被调用一次
  • 初始化使得DOM节点应该进行到这里。
  • 这个方法是用于在服务器渲染上的唯一方法
  • 这个方法因为是在渲染之前被调用,也是惟一一个可以直接同步修改state的地方。

  • 通常在这里进行ajax请求
  • 如果要初始化第三方的dom库,也在这里进行初始化。只有到这里才能获取到真实的dom.
static getDerivedStateFromProps【!】
  • static getDerivedStateFromProps(nextProps, prevState)
  • 其周期函数是静态方法,务必加上static关键词;
  • 在组件创建时和更新时的render方法之前调用
  • 返回一个对象来更新状态,或者返回null来不更新任何内容。

  • Father.jsx
import React, { Component } from "react";
import Son from "./Son";
class Father extends Component {
    render() {
        return (
            <div>
                <Son val={1423}></Son>
            </div>
        );
    }
}

export default Father;
  • Son.jsx
import React, { Component } from "react";

class Son extends Component {
    //  初始化状态
    state = {
        num: 0,
        a: 0
    };
    render() {
        return (
            <div>
                <div>子组件:当前自身的state是{this.state.num}</div>
                <button onClick={this.ck.bind(this)}>给a+1</button>
            </div>
        );
    }

    // 场景:希望本组件自身的状态与props同步,这个时候就可以使用getDerivedStateFromProps周期
    static getDerivedStateFromProps(props, state) {
        console.log('走了xxxxx');
        // 在这里进行赋值
        if (props.val === state.num) {
            // 相等,不需要赋值
            return null;
        } else {
            // 不相等,则需要赋值
            return {
                num: props.val,
            };
        }
    }

    ck() {
        this.setState((state) => {
            return {
                a: state.a + 1,
            };
        });
    }
}

export default Son;
  • 运行结果
    【React】生命周期详解_第9张图片

2.2、更新阶段

  • 当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
  1. static getDerivedStateFromProps
  2. shouldComponentUpdate
  3. render
  4. getSnapshotBeforeUpdate
  5. componentDidUpdate
componentDidUpdate【!】
  • componentDidUpdate(prevProps, prevState, snapshot)
  • 调用:在组件更新之后
  • 注意:首次渲染不会执行此方法。

componentDidUpdate()用法

  • 当组件更新时,可以在此处对 DOM 进行操作。
  • 如果你对更新前后的 props 进行了比较,也可以选择在此处进行网络请求
  • 例如,如果props没有改变,则可能不需要网络请求
  • 可以在里面直接调用 setState()
  • 但是 setState() 得在条件语句里使用
  • 避免导致额外的重新渲染

与componentDidUpdate()联用

  • 如果组件实现getSnapshotBeforeUpdate()生命周期
  • 则它返回的值将作为第三个参数【snapshot】传递给componentDidUpdate()
  • 否则,这个参数是undefined

与shouldComponentUpdate()联用

  • 如果 shouldComponentUpdate() 返回值为 false
  • 则不会调用 componentDidUpdate()。
shouldComponentUpdate
  • shouldComponentUpdate(nextProps, nextState)
  • 根据 shouldComponentUpdate() 的返回值
  • 判断组件的输出是否受stateprops的影响。
  • 默认行为是 state 每次发生变化组件都会重新渲染。

  • 当 props 或 state 发生变化时
  • shouldComponentUpdate会在渲染执行之前被调用
  • 返回值默认为 true。
  • 首次渲染或使用 forceUpdate() 时不会调用该方法。

shouldComponentUpdate() 返回 false

  • componentWillUpdaterender
  • componentDidUpdate不会被调用。
getSnapshotBeforeUpdate
  • getSnapshotBeforeUpdate(prevProps, prevState)
  • 在最近一次渲染输出(提交到 DOM 节点)之前调用
  • 它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)
  • 这个生命周期返回的任何值都将作为参数传递给componentDidUpdate()。

作用地点:

  • 此用法并不常见,但它可能出现在 UI 处理中
  • 如需要以特殊方式处理滚动位置的聊天线程等。
class ScrollingList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 我们是否在 list 中添加新的 items ?
    // 捕获滚动​​位置以便我们稍后调整滚动位置。
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop;
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // 如果我们 snapshot 有值,说明我们刚刚添加了新的 items,
    // 调整滚动位置使得这些新 items 不会将旧的 items 推出视图。
    //(这里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)
    if (snapshot !== null) {
      const list = this.listRef.current;
      list.scrollTop = list.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.listRef}>{/* ...contents... */}</div>
    );
  }
}

2.3、卸载阶段

  • 当组件从 DOM 中移除时会调用如下方法:
componentWillUnmount【!】
  • componentWillUnmount()
  • 调用:在组件卸载之前
  • 作用:执行一些清理工作
  • 例如
  • 清除组件中使用的定时器,
  • 取消网络请求或清理在 componentDidMount 中创建的任何监听
  • 清除 componentDidMount 中手动创建的DOM元素

2.4、错误处理

  • 当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:
componentDidCatch
  • componentDidCatch(error, info)
  • 此生命周期在后代组件抛出错误后被调用。
  • error —— 抛出的错误。
  • info —— 带有 componentStack key 的对象
  • componentDidCatch() 会在“提交”阶段被调用,因此允许执行副作用。
  • 它应该用于记录错误之类的情况
static getDerivedStateFromError
  • static getDerivedStateFromError(error)
  • 此生命周期会在后代组件抛出错误后被调用
  • 它将抛出的错误作为参数,并返回一个值以更新 state

你可能感兴趣的:(React,全家桶,react,生命周期)