day-076-seventy-six-20230523-Hooks组件useState()-Hooks组件useEffect()-Hook函数组件说明

Hooks组件useState()

React Hooks 组件化开发

React 组件分类

函数组件

不具备状态、ref、周期函数等内容,第一次渲染完毕后,无法基于组件内部的操作来控制其更新,因此称之为静态组件!但是具备属性及插槽,父组件可以控制其重新渲染!渲染流程简单,渲染速度较快!基于FP函数式编程思想设计,提供更细粒度的逻辑组织和复用!

FP-Funtional Programming-函数式编程思想。oop-面向对象编程思想。pop-面向过程。 类组件

具备状态、ref、周期函数、属性、插槽等内容,可以灵活的控制组件更新,基于钩子函数也可灵活掌控不同阶段处理不同的事情!渲染流程繁琐,渲染速度相对较慢!基于OOP面向对象编程思想设计,更方便实现继承等! React Hooks组件,就是基于React中新提供的Hook函数,可以让函数组件动态化!

Hook 函数概览

Hook是React16.8的新增特性!并且只能运用到函数组件中!

Hook[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VZtMC5FY-1684857369285)(./ReactHooks的所有HookAPI.jpg)] React Hooks组件化开发:

2023前端当下和未来的主流编程思想:函数式编程FP,也叫去面向对象化。在React开发中,目前最常用的组件类型,应该是函数式组件,类组件偶尔使用。而React提供的各种HookAPI,就是让函数组件可以像类组件一样,具备状态、周期函数、Ref 等操作,让函数组件从静态化转为动态化。所有的HookAPI特点:

都是以useXxx的格式命名的。只能用在函数组件中,在类组件中用不了。

类组件中也没必要用。 在函数式组件内部中,this为undefined。 HookAPI:

useState():在函数组件中使用状态,并且提供修改状态让视图更新的办法。

let [xxx,setXxx] = useState(初始值)

每一次执行useState()就创建一个状态。

如果需要创建多个状态,则可以让其执行多次,或者使用useReducer()这个Hook函数。 useState()执行的返回结果是一个数组:[当前状态值,修改状态并让视图更新的函数]。

声明一个叫做xxx的变量,用于存储状态值。setXxx(值):把状态值修改为传递的值,并且让组件更新。 Hook组件与类组件的对比:

样式组件/src/views/VoteBoxStyle.jsx: import styled from "styled-components"

const VoteBoxStyle = styled.div`

box-sizing: border-box;

margin: 20px auto;

padding:10px 20px;

width: 300px;

border: 1px solid #DDD;

.title{

display: flex;

justify-content: space-between;

align-items: center;

line-height: 50px;

font-size: 18px;

border-bottom:1px dashed #DDD;

span{

color: #ff4d4f;

}

}

.main-box{

padding: 10px 0;

p{

font-size: 14px;

line-height: 30px;

}

}

.footer-box{

.ant-btn{

margin-right: 10px;

}

}

`

export default VoteBoxStyle

Hook组件/src/views/VoteBox.jsx: import React, { useState } from "react";

import { Button } from "antd";

import VoteBoxStyle from "./VoteBoxStyle";

export default function VoteBox() {

// console.log("this", this);//undefined;

// console.log(useState(6));

let [supNum, setSupNum] = useState(6);

let [oppNum, setOppNum] = useState(2);

let total = supNum + oppNum;

let ratio = `--`;

if (total > 0) {

ratio = `${((supNum / total) * 100).toFixed(2)}%`;

}

// 定义普通函数;

const handle = (type) => {

if (type === `sup`) {

setSupNum(supNum + 1)

return

}

setOppNum(oppNum + 1)

}

return (

React其实也不难!

{total}

支持人数:{supNum} 人

反对人数:{oppNum} 人

支持比率:{ratio}

);

}

Hook组件中关于useState()中的Xxx与setXxx在更新和渲染时内存变动.jpg主要用到作用域链、闭包。 类组件/src/views/VoteBox.jsx: import React from "react"

import { Button } from 'antd'

import VoteBoxStyle from "./VoteBoxStyle"

console.log(React);

// 基于类组件完成需求:

export default class VoteBox extends React.Component {

state = {

supNum: 6,

oppNum: 2,

}

handle = (type) => {

let { supNum, oppNum } = this.state;

if (type === `sup`) {

this.setState({

supNum: supNum + 1

})

return

}

this.setState({

oppNum: oppNum + 1

})

}

render() {

let { supNum, oppNum } = this.state;

let total = supNum + oppNum;

let ratio = `--`;

if (total > 0) {

ratio = `${((supNum / total) * 100).toFixed(2)}%`

}

return

React其实也不难!

{total}

支持人数:{supNum} 人

反对人数:{oppNum} 人

支持比率:{ratio}

}

}

主要用到面向对象,this指向,原型链。

模拟useState的源码

// 模拟useState的源码

let state //这个是用于存储当前状态值的。

const useState = function useState(initialValue){

// 只有第一次执行useState时,才会给状态赋值初始值。

if(typeof state ==='undefined'){

state = initialValue

}

// 修改状态的函数

const change = (value)=>{

state = value

// 让视图更新

// ...

}

return [state,change]

}

也就是更新时的useState(initialValue),不会再走初始值了。

但是在更新执行那个函数时,依旧会执行Hook函数,只是这个Hook函数返回的值变化了。

也就是说:调用的Hook方法该重新执行还是重新执行。 import React, { useState } from "react";

import { Button } from "antd";

import VoteBoxStyle from "./VoteBoxStyle";

export default function VoteBox() {

let [supNum, setSupNum] = useState(6);//第一次渲染时,会执行这个函数`useState(6)`,返回的值为[6,修改状态值的函数]。而到了非第一次渲染的更新时,依旧还是会执行`let [supNum, setSupNum] = useState(6)`,只是此时`useState(6)`返回的值变成了`[上一次传递给函数的状态值,修改当前状态值的函数]`

return (

React其实也不难!

{supNum}

);

}

函数组件的闭包

函数组件的每一次渲染和更新,都是把函数重新执行,产生一个新的闭包,多次调用组件并多次渲染,都是在自己的闭包中,进行相关的处理,相互之间不会有影响。

每一次函数执行,props始终都在,都是父组件传的属性值。每一次函数执行,函数中的代码都会重新自上而下执行一遍。

调用的Hook方法该重新执行还是重新执行。如果遇到的是创建函数:则每个闭包中都会重新创建一个新的函数,而此函数的作用域就是当前这个闭包。

新创建的函数依旧还是会被重新创建,得到一个新的堆内存地址。 如果是创建私有变量:则每个闭包中都有这个变量,用于存储闭包作用域中属于自己的值。如果是useState:则第一次渲染会让状态等于初始值,后期再更新的时候,状态等于最新状态值。… 如果要判断某个变量是什么值,需要缕清楚其作用域链(或者是其属于那个闭包),才能知道值具体是多少!

import React, { useState } from "react";

import { Button } from "antd";

import VoteBoxStyle from "./VoteBoxStyle";

export default function VoteBox() {

// console.log("this", this);//undefined;

// console.log(useState(6));

let [supNum, setSupNum] = useState(6);

let [oppNum, setOppNum] = useState(2);

let total = supNum + oppNum;

let ratio = `--`;

if (total > 0) {

ratio = `${((supNum / total) * 100).toFixed(2)}%`;

}

// 定义普通函数;

const handle = (type) => {

if (type === `sup`) {

setSupNum(supNum + 1)

setTimeout(()=>{

console.log(supNum);//依旧是当前闭包中的值。

})

return

}

setOppNum(oppNum + 1)

}

return (

React其实也不难!

{total}

支持人数:{supNum} 人

反对人数:{oppNum} 人

支持比率:{ratio}

);

}

组件中多个状态和处理

useState中多个状态的处理:

官方推荐:需要多个状态,则执行useState,创建多个状态及修改状态的方法,让每个状态单独管理。 // 多状态的处理-多次useState()。

import React, { useState } from "react";

import { Button } from "antd";

import VoteBoxStyle from "./VoteBoxStyle";

export default function VoteBox() {

let [supNum, setSupNum] = useState(6);

let [oppNum, setOppNum] = useState(2);

// 定义普通函数;

const handle = (type) => {

if (type === `sup`) {

setSupNum(supNum + 1)

return

}

setOppNum(oppNum + 1)

}

return (

React其实也不难!

{supNum + oppNum}

支持人数:{supNum} 人

反对人数:{oppNum} 人

);

}

好处:

状态分离,增删都方便处理。 当前也可以执行一次useState,只不过创建的这个状态,需要是一个对象,对象中包含了需要的其它状态值。 // 单状态的处理-单次调用useState()-传递的为对象。

import React, { useState } from "react";

import { Button } from "antd";

import VoteBoxStyle from "./VoteBoxStyle";

export default function VoteBox() {

let [state, setState] = useState({

supNum: 6,

oppNum: 2

});

const handle = (type) => {

if (type === `sup`) {

setState({ ...state, supNum: state.supNum + 1 })

return

}

setState({ ...state, oppNum: state.oppNum + 1 })

}

return (

React其实也不难!

{state.supNum + state.oppNum}

支持人数:{state.supNum} 人

反对人数:{state.oppNum} 人

);

}

但是这种方式我们不推荐:

基于useState获取的修改状态方法,setXxx,在修改状态值的时候,不支持部分状态的更改。 setState({ supNum: state.supNum + 1 })

只有React.prototype.setState才支持部分状态更改。 setXxx()中传递什么值,就把状态值整体改为什么。 setState({ supNum: state.supNum + 1 })//会直接用这个`{ supNum: state.supNum + 1 }的内存地址`把旧的state对象的内存地址改了。也就是说,旧state对象被完全修改了。

如何解决?

把要修改的值浅拷贝一份。 setState({ ...state,supNum: state.supNum + 1})

在修改成为新的状态对象之前,先把现有的状态信息浅拷贝一份,在修改的时候,没改的用原始值,改变的地方用用新改的值。 setState({

...state,//把要修改的值浅拷贝一份。

supNum: state.supNum + 1

})

隋性初始state值

如果状态的初始值需要计算,而且是经过复杂计算、消耗很多性能算出来的。而且只需要第一次使用,不想在后续更新时再把逻辑走一遍。 // 隋性初始化初始状态值-正常的用非函数的值来初始化。

import React, { useState } from "react";

import { Button } from "antd";

import VoteBoxStyle from "./VoteBoxStyle";

export default function VoteBox() {

// 如果状态的初始值需要计算,而且是经过复杂计算、消耗很多性能算出来的。而且只需要第一次使用。

let ran = Math.round(Math.random() * 9 + 1)//每次更新时都会执行一遍。

console.log('ran',ran);//每次更新时都会执行一遍。

let [supNum, setSupNum] = useState(ran);

let [oppNum, setOppNum] = useState(2);

// 定义普通函数;

const handle = (type) => {

if (type === `sup`) {

setSupNum(supNum + 1)

return

}

setOppNum(oppNum + 1)

}

return (

React其实也不难!

{supNum + oppNum}

支持人数:{supNum} 人

反对人数:{oppNum} 人

);

}

可以发现,初始化状态值所用的代码每次更新时都会执行一遍。

但是执行后的结果,在更新时都不再被需要了。造成性能浪费。 解决方案:执行useState()初始化状态时,传递给useState()的是一个函数。

此函数只对第一次执行useState(),并把返回值,作为状态的初始值。后续更新时,该函数都不再执行。 // 隋性初始化初始状态值-基于函数的方式处理。

import React, { useState } from "react";

import { Button } from "antd";

import VoteBoxStyle from "./VoteBoxStyle";

export default function VoteBox() {

let [supNum, setSupNum] = useState(() => {

// 如果状态的初始值需要计算,而且是经过复杂计算、消耗很多性能算出来的。而且只需要第一次使用,我们基于函数的方式处理。

//此函数只对第一次执行useState(),并把返回值,作为状态的初始值。

let ran = Math.round(Math.random() * 9 + 1)

console.log('ran', ran);

//...

return ran

});

let [oppNum, setOppNum] = useState(2);

// 定义普通函数;

const handle = (type) => {

if (type === `sup`) {

setSupNum(supNum + 1)

return

}

setOppNum(oppNum + 1)

}

return (

React其实也不难!

{supNum + oppNum}

支持人数:{supNum} 人

反对人数:{oppNum} 人

);

}

useState()返回的数组中的修改值函数setXxx()的异步更新机制

import { useState } from "react";

import { Button } from "antd";

export default function Demo() {

console.log('render')

let [x, setX] = useState(10);

let [y, setY] = useState(20);

let [z, setZ] = useState(30);

const handle = () => {

setX(x + 1);

setY(y + 1);

setZ(z + 1);

};

return (

x:{x}-y:{y}-z:{z}

);

}

useState()返回的数组中的修改值函数setXxx()的异步更新机制.jpg基于useState创建状态,会拿到修改状态的方法setXxx()。

在React18中,执行setXxx()是异步去修改状态和让视图更新的。

底层机制:updater更新队列机制:

当然也可以基于flushSync让其变为类似于同步的效果(立即刷新渲染队列)! 不论修改状态是同步还是异步,此处获取的x的值,永远都不会是最新修改的。用的都是现在闭包中的值。最新修改的状态值,只能在下一个闭包中获取!

即便把修改状态的操作,基于flushSync()处理了,也仅仅是让其立即更新渲染一次,在它的下面,依然无法获取最新修改的状态值。

而在类组件中:

我们还可以基于this.setState()中的callback()函数,通过this.state.xxx获取最新状态值。 this.setState({x:x+1},()=>{

//this.state可以拿到最新的x。

console.log(this.state.x);

})

或者基于flushSync()把状态修改变为类似于同步效果,然后在flushSync()下面,就可以获取最新状态值。 this.setState({x:x+1},()=>{

//this.state可以拿到最新的x。

console.log(this.state.x);

})

import { useState } from "react";

import { Button } from "antd";

import { flushSync } from "react-dom";

export default function Demo() {

console.log("render"); //只打印一次,证明setXxx()是异步修改状态的。

let [x, setX] = useState(10);

let [y, setY] = useState(20);

let [z, setZ] = useState(30);

console.log("新一次渲染的一次闭包中的x", x);

const handle = () => {

flushSync(() => {

setX(x + 1);//会先更新,但下方的x依旧是旧的,只有新的闭包中的x才是本次闭包操作中的x+1;

})

setY(y + 1);

setZ(z + 1);

console.log("点击后x", x);//不论修改状态是同步还是异步,此处获取的x的值,永远都不会是最新修改的。用的都是现在闭包中的值。最新修改的状态值,只能在下一个闭包中获取!即便把修改状态的操作,基于flushSync处理了,也仅仅是让其立即更新渲染一次,在它的下面,依然无法获取最新修改的状态值。

// 而在类组件中,我们还可以基于this.setState中的callback函数,通过this.state.xxx获取最新状态值。或者基于flushSync()处理等。

};

return (

x:{x}-y:{y}-z:{z}

);

}

在React16中,setXxx有时候是异步操作,有时候是同步操作。

在其它异步操作中,它本身是同步的。在非异步操作中,它本身是异步操作的! 在同步异步性的处理上,和类组件中的this.setState()/this.forceUpdate()是保持一致的!

useState()返回的数组中的修改值函数setXxx()的累计更新

直接调用setXxx(),不能直接修改值。

并且在同步的for循环过程中,x依旧都是旧闭包作用域中的x。 import { useState } from "react";

import { Button } from "antd";

import { flushSync } from "react-dom";

export default function Demo() {

console.log("render"); //只打印一次,证明setXxx()是异步修改状态的。

let [x, setX] = useState(10);

let [y, setY] = useState(20);

let [z, setZ] = useState(30);

console.log("新一次渲染的一次闭包中的x", x);

const handle = () => {

for (let i = 0; i < 10; i++) {

setX(x +1);

}

};

return (

x:{x}-y:{y}-z:{z}

);

}

基于函数来做到更新: import { useState } from "react";

import { Button } from "antd";

import { flushSync } from "react-dom";

export default function Demo() {

console.log("render"); //只打印一次,证明setXxx()是异步修改状态的。

let [x, setX] = useState(10);

let [y, setY] = useState(20);

let [z, setZ] = useState(30);

console.log("新一次渲染的一次闭包中的x", x);

const handle = () => {

for (let i = 0; i < 10; i++) {

setX(prev => {

return prev + 1

});

}

};

return (

x:{x}-y:{y}-z:{z}

);

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l4Sg3kmD-1684857369287)(./useState()]返回的数组中的修改值函数setXxx()的累计更新.jpg)

useState()返回的数组中的修改值函数setXxx()的新旧值比对优化

基于setXxx修改状态,函数组件内部做了优化机制:

基于Object.is()方法,对新老状态值做对比,如果发现是一致的,则视图不更新。

类似于设置了优化处理的shouldComponentUpdate()钩子函数。 import { useState } from "react";

import { Button } from "antd";

export default function Demo() {

console.log("render");

let [x, setX] = useState(10);

let [y, setY] = useState(20);

let [z, setZ] = useState(30);

const handle = () => {

console.log("click");

setX(10);//执行了该函数,但不会更新视图。可以看作内部用了Object.is()做了比对,如果是true,就不更新视图。

};

return (

x:{x}-y:{y}-z:{z}

);

}

Hooks组件useEffect()

在函数组件中使用生命周期函数

useEffect()/useLayoutEffect() 在函数组件中使用生命周期函数

useEffect(callback)

在第一次渲染完毕和每一次组件更新完毕后,触发callback执行。

等价于componentDidMount()和componentDidUpdate()。 import { useState, useEffect, useLayoutEffect } from "react";

import { Button } from "antd";

export default function Demo() {

console.log('render');

let [x, setX] = useState(10);

let [y, setY] = useState(20);

let [z, setZ] = useState(30);

const handle = () => {

setX(x + 1);

setY(y + 1);

setZ(z + 1);

console.log('click');

};

useEffect(() => {

console.log('AA');

})

return (

x:{x}-y:{y}-z:{z}

);

}

useEffect(callback,[])

只在第一次渲染完毕后触发执行。

等价于componentDidMount()。 useEffect(() => {

console.log('componentDidMount');

}, [])

useEffect(callback,[依赖的状态])

在第一次渲染完毕后触发执行,以及依赖的状态发生改变后触发执行。

类似于Vue2中watch监听器。 // 类似于监听器,监听了x与y。

useEffect(() => {

console.log('@3 第一次渲染完毕 与 x或y状态改变');

}, [x, y])

useEffect(返回一个函数的callback,[])组件销毁之前做些事情。

给useEffect()传递的callback函数,其内部可以不写返回值。

如果设置返回值,则必须返回一个函数! useEffect(() => {

return () => {

console.log('@4 组件销毁之前');

}

}, [])

useEffect(() => {

//第一次渲染完毕做的事情;

return () => {

//组件销毁之前做的事情;

}

}, [])

不写数组或者设置依赖项:

useEffect(返回一个函数A的callback) 但凡有状态更新,会先把上一次闭包中callback返回的函数A执行,之后才执行本次闭包中的返回一个函数A的callback。 useEffect(返回一个函数A的callback,[依赖的状态]) 依赖的状态发生改变,会先把上一次闭包中callback返回的函数A执行,之后才执行本次闭包中的返回一个函数A的callback。 useEffect(() => {

return ()=>{

//但凡有状态更新,或者依赖的状态发生改变,在组件更新之前,会先把此函数执行。-->通俗理解:产生新的闭包之前,处理的事情。

}

},不写数组或者设置依赖项)

useEffect()内部回调处理机制

每一次函数组件更新/渲染,除了产生一个新的闭包外,还会生成一个新的effect链表!

effect链表:存储基于useEffect创建的callback(),或者是callback返回的函数,以及依赖的状态。

组件渲染中:

每次遇到useEffect(callback,依赖项/空数组/没有),基于MountEffect方法,把依赖项和callback放在effect链表中。 组件渲染完毕:会基于UpdateEffect方法通知effect链表中的callback,按照各自的特征,去逐一执行! callback执行的时候,其宿主上下文,就是本次视图渲染产生的闭包! [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IQTvnLlp-1684857369288)(./useEffect()]内部回调处理机制.jpg)

Hook函数组件说明

Hook函数组件的每一次更新和渲染,都是让函数重新执行。

产生新的私有上下文,代码会自上而下执行一遍,最后返回新的VirtualDOM。

如果是第一次渲染:

当前函数组件当前执行时产生了私有上下文1。当前函数组件当前执行时产生的私有上下文1中的this一般是undefined。

因为经过React处理过了。 通过当前函数组件第一个形参得到父组件传递的props。通过执行useState(),得到初始状态值和修改该初始状态值的函数。

返回的初始状态值是第一次传入useState()函数内的值。虽然看起来初始状态值没什么变化,但React内部已经帮你做了很多操作。

比如会返回一个修改状态值的函数,以及记录了当前的作用域、当前所在函数、当前父节点之类的操作。

同时,在调用修改状态值的函数时,还会重新执行一遍当前函数,产生的虚拟DOM也可以在父组件中被更新。 自上而下执行一遍,中间可能会创建一些局部函数,会返回一个虚拟DOM,而父组件会将它缓存下来,并渲染到页面上。

这些创建的局部函数中,上级作用域链是当前函数组件当前执行时产生的私有上下文1,也就意味着,如果它内部找不到值,会在当前函数组件当前执行时产生的私有上下文1中查找。同时父组件还会缓存当前返回出去的虚拟DOM,以便下次更新时进行比较。 该组件中执行的方法和使用的值,基本上都是当前函数组件当前执行时产生的私有上下文1中的值或得到的函数或新创建的函数。 如果是后续更新,重新自上而下执行一遍,但Hook组件中提供的Hook函数,可以让返回值变动,进而让函数返回虚拟DOM产生新的变化。

当前函数组件当前执行时产生了私有上下文2。 当前函数组件当前执行时产生的私有上下文2中的this一般是undefined。

因为经过React处理过了。 通过第一个形参得到父组件传递的props。

函数组件的每次更新,都会把第一次渲染时获取的属性,在后期每一次执行中,传递进来。 通过执行useState(),得到最新状态值和修改该最新状态值的函数。

返回的最新状态值是上一次传入函数内的值。虽然看起来初始状态值没什么变化,但React内部已经帮你做了很多操作。

比如会返回一个修改状态值的函数,以及记录了当前的作用域、当前所在函数、当前父节点、当前props之类的操作,同时,在调用修改状态值的函数时,还会重新执行一遍当前函数,产生的虚拟DOM也可以在父组件中被更新。 自上而下执行一遍,中间可能会创建一些局部函数,会返回一个虚拟DOM,而父组件会将它缓存下来,并渲染到页面上。

这些创建的局部函数中,上级作用域链是当前函数组件当前执行时产生的私有上下文1,也就意味着,如果它内部找不到值,会在当前函数组件当前执行时产生的私有上下文1中查找。同时父组件还会缓存当前返回出去的虚拟DOM,以便下次更新时进行比较。 该组件中执行的方法和使用的值,基本上都是当前函数组件当前执行时产生的私有上下文1中的值或得到的函数或新创建的函数。 import { useState, useEffect, useLayoutEffect } from 'react'

import { Button } from 'antd'

export default function Demo() {

let [x, setX] = useState(10),

[y, setY] = useState(20),

[z, setZ] = useState(30)

useEffect(() => {

console.log('@1 第一次渲染完毕/更新完毕', x, y, z)

return () => {

console.log('@5 组件更新之前', x, y, z);

}

})

useEffect(() => {

console.log('@2 第一次渲染完毕', x, y, z)

setTimeout(() => {

console.log('哈哈哈', x, y, z) //获取的永远是闭包1中的状态值 10/20/30

}, 5000)

return () => {

console.log('@4 组件销毁之前', x, y, z);

}

}, [])

useEffect(() => {

console.log('@3 第一次渲染完毕 & x/y状态改变', x, y, z)

}, [x, y])

return

{x} - {y} - {z}

}

Hook组件中关于useState()中的Xxx与setXxx在更新和渲染时内存变动.jpg

import { useState, useEffect, useLayoutEffect } from "react";

import { Button } from "antd";

export default function Demo() {

console.log('render');

let [x, setX] = useState(10);

let [y, setY] = useState(20);

let [z, setZ] = useState(30);

// useEffect(() => {

// console.log('@1 第一次渲染完毕 或 更新完毕');

// })

// useEffect(() => {

// return ()=>{

// console.log('@5 组件更新之前');

// }

// })

useEffect(() => {

console.log('@1 第一次渲染完毕 或 更新完毕', x, y, z);

return () => {

console.log('@5 组件更新之前', x, y, z);

}

})

// useEffect(() => {

// console.log('@2 第一次渲染完毕');

// }, [])

// useEffect(() => {

// return () => {

// console.log('@4 组件销毁之前');

// }

// }, [])

// 类似于监听器,监听了x与y。

useEffect(() => {

console.log('@3 第一次渲染完毕 与 x或y状态改变', x, y, z);

}, [x, y])

useEffect(() => {

console.log('@2 第一次渲染完毕', x, y, z);

setTimeout(() => {

console.log(`延时`, x, z, y);//因为该useEffect只在第一次执行,所以依旧是第一次的值。x-10,y-20,z-30;

}, 5000)

return () => {

console.log('@4 组件销毁之前', x, y, z);

}

}, [])

return (

x:{x}-y:{y}-z:{z}

);

}

进阶参考

相关链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: