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}
修改x
修改y
修改z
);
}
进阶参考
相关链接
发表评论