React生命周期&useState、useRef、useEffect

345 阅读5分钟

1、React组件生命周期

组件的生命周期是指,组件对象从创建到死亡所经历的特定的阶段,函数式组件中包含一系列钩子函数(生命周期回调函数),在特定的时刻会自动调用

全称:生命周期函数或者是生命周期钩子,本质就是组件内的一些方法。

概念:生命周期函数指在某一时刻组件会自动调用执行的函数

2、完成挂载阶段——ComponentDidMount

执行顺序:(均自动执行)

  1. constructor 构造器在组件调用时首次执行,目的是给组件实例化对象身上做初始化赋值(this)
  2. render (组件渲染页面,state值发生改变就会触发render重新调用,n+1次)
  3. ComponentDidMount (将组件挂载到root根容器元素对象身上)

父构造 —> 父render —> 子构造 —> 子render —> 子完成挂载 —> 父完成挂载

【应用】:定时器、ajax请求(axios)、操作DOM元素、订阅频道(兄弟组件之间的通信)

当我们希望一打开页面,页面上就挂载着某些数据,此时就要将ajax请求数据放在组件挂载完成阶段,且如果数据是变化的,那么就将该数据设置为组件的状态值。

3、更新阶段——ComponentDidUpdate

1、只有子组件状态值更新:

子render —> 子ComponentDidUpdate(不会对父组件产生影响)

2、父组件中状态值更新,且嵌套调用子组件时:

父render —> 子render —> 子更新 —> 父更新

【应用】:状态值改变、组件接收到新的Props

4、即将卸载阶段——ComponentWillUnMount

当每一个类组件中,有内容更改时,都会自动触发卸载阶段的钩子函数。

1、只有一个组件内容更改时:

自己的构造 —> 自己的render —> 自己的卸载 —> 自己的挂载 (将之前的内容卸载之后,挂载一个新的)

2、父组件中内容更改,且嵌套调用子组件时:

父构造 —> 父render —> 子构造 —> 子render —> 父卸载 —> 子卸载 —> 子挂载 —> 父挂载

【应用】:清除定时器 、取消订阅

5、案例——定时器

import React, { Component } from 'react';
import moment from 'moment';

export default class Timer extends Component {
    state = {
        timestr: moment().format('YYYY-MM-DD HH:mm:ss')
    }
    constructor() {
        super();
        console.log('Timer已构造...');
        this.timeid = null;
    }
    render() {
        let { timestr } = this.state;
        console.log('Timer--render...')
        return (
            <div>
                当前的系统时间是:{timestr}
            </div>
        )
    }
    componentDidMount() {
        this.timeid = setInterval(() => {
            this.setState({
                timestr: moment().format('YYYY-MM-DD HH:mm:ss')
            })
        }, 1000);
        console.log('Timer完成了挂载...')
        console.log(this.timeid)
    }
    componentDidUpdate() {
        console.log('Timer完成了更新...')
    }
    componentWillUnmount() {
        clearInterval(this.timeid)
        console.log('Timer将要被卸载...')
    }
}

6、案例——评论列表

7、Hook

钩子,本质就是函数,能够在函数组件中使用状态生命周期函数的功能。

【注】Hook基本完全替代了类组件的语法,后面的React项目就完全是函数式组件了。

(1)useState

用来定义状态数据,可以多次调用,可产生多个状态数据。

import React , { useState } from 'react';
//函数式组件是没有实例化对象的,所以在函数内不能出现this这样的语法

export default function Counter() {
    //useState(状态数据),状态数据中可以存放Number、String、Boolean、Array
    //useState方法返回的是一个数组,
    //数组的第一个元素:状态数据(useState方法传过来的实参)
    //数组的第二个元素:更新状态值的函数结构体(类似于setState方法)
    //数组的解构
    let [num, setNum] = useState(100);

    let addCount = function () {
        num++;
        //将状态值用setNum函数重新设置
        setNum(num);
    }

    return (
        <div>
            <p>计数器:{num}</p>
            <button onClick={addCount}>点击+1</button>
        </div>
    )
}

(2)useRef

功能和类式组件中的createRef()方法相似,可以帮助我们在函数式组件中方便获取真实的DOM元素对象。 【注】ref是虚拟DOM元素的属性,但是使用ref是为了获取所在的真实DOM元素对象。

import React, { useRef } from 'react'

export default function InputValue() {
    let unameIpt = useRef();
    let showIptValue = function () {
        console.log(unameIpt.current.value);
    }
    return (
        <div>
            <p>用户名:<input type="text" ref={unameIpt} /></p>
            <button onClick={showIptValue}>点击获取输入框的内容</button>
        </div>
    )
}

(3)useEffect

useEffect是一个高阶函数,可以在一个组件中多次使用

相当于componentDidMount(组件挂载完成)componentDidUpdate(组件更新完成)componentWillUnmount(组件将要卸载之前)三者的结合体。

useEffect方法可以传递两个参数,根据所填参数不同可表示不同的生命周期阶段:

  • 第一个参为函数结构,必填
  • 第二个参数可为数组,选填
import React, { useState, useRef, useEffect } from 'react'

export default function MyComponent() {
//1、第一个参数是函数结构,第二个参数是空数组 ===> 表示ComponentDidMount 组件挂载完成
    useEffect(() => {
        console.log('组件挂载完成了1');
    }, []);
    useEffect(() => {
        console.log('组件挂载完成了2');
    }, []);
    useEffect(() => {
        console.log('组件挂载完成了3');
    }, []);
    //一打开页面,组建完成挂载,自动一次执行这三条语句


//2-1、第一个是函数结构,不传递第二个参数 ===> 表示表示ComponentDidMount + ComponentDidUpdate
//   即:组件挂载完成以及组件状态更新
	useEffect(() => {
        console.log('组件挂载完成 & 组件更新完成');
    })
    //组件挂载完成会执行一次,组件每次更新状态值也会执行一次

    let [num, setNum] = useState(100);
    let [isLogin, setisLogin] = useState(true); 
    
//2-2、第一个是函数结构,第二个是非空数组,数组中传入state的状态数据 ===> 当这个状态数据被更新时才执行回调函数
//【注】数组的参数如果没有任何值的话,空数组,表示什么状态数据都不更新,回调函数不执行
	useEffect(() => {
        console.log('当islogin状态值发生改变时,组件挂载完成&组件更新完成')
    }, [isLogin])		


//3、模拟ComponentwillUnmount
//在脚手架中当尝试修改组件之后,react会将组件先卸载,然后再来一个新的重新挂载
//在useEffect方法中返回一个函数
	useEffect(() => {
        console.log('组件即将被卸载......');
    })
    
    let addCount = () => {
        num += 10;
        setNum(num);
    }
    let changeLogin = () => {
        isLogin = !isLogin;
        setisLogin(isLogin);
    }
    return (
        <div>
            <p>计数器:{num}</p>
            <button onClick={addCount}>点我+10</button>
            <hr />
            <p>登录状态:{isLogin ? '已登录' : '滚去登录'}</p>
            <button onClick={changeLogin}>切换登录状态</button>
        </div>
    )
}

【注意点】:

useEffect方法的第一个参数的函数是一个同步函数,不接收async参数,(原因是如果设置了async,返回值就成了promise对象,非一个函数了)

如果想要在useEffect()方法中添加异步代码,需要在函数中在单独声明一个函数

useEffect(() => {
     //函数声明
     async function getData() {
         let res = await 100;
     }
     //函数调用,一定要有!
     getData();
})