前言
从react16.8开始,react跨入了一个新时代。可以这样说,16.8版本之前,react的使用者十之八九都是类组件操作数据,函数组件渲染数据,一动一静结合起来使用;但是react16.8版本之后,react项目完全可以脱离类组件编程,react-hooks迎来了react的新时代,开发者不再为this指向的问题感到烦恼,在复用性上面也解决了类组件的嵌套逻辑复杂的情况,这边文章主要介绍 函数式组件原理 以及 react-hooks的功能源码。
函数式组件分析
顾名思义,函数式组件简单来说就是一个函数返回一个html,然后基于babel-preset-react-app把jsx编译成createElment格式的函数语法树,把createElment执行创建出虚拟dom,基于root.render函数方法把虚拟dom转换成真是dom,最后浏览器渲染和绘制真实dom,但是类组件里面有生命周期可以管理数据的动态,而函数是静态的,内部不能管理数据的变化,所以react开发了hooks函数,hooks函数的作用就是让静态的函数能够动态化,后面就让我们慢慢来了解react-hook函数的功能及用法;
react-hooks函数原理及使用
1. useState 更新状态函数
useState是React Hook函数之一,也是用的最多的一个,目的是在函数组件中使用状态,并且后期基于状态的修改,可以让组件更新;
注意:函数组件的每一次渲染(或者是更新),都是把函数(重新)执行产生一个全新的作用域(或者说是私有上下文),所以函数内部的代码也需要重新执行;(原理就是产生一个新的闭包,所以js没有学好的同学一定得先回头复习js的运行原理部分,否则后面理解函数式组件原理会很吃力)。
// 使用方法
import { useState } from 'react
let [num, setNum] = useState(0);
//执行useState,传递一个初始的状态值,返回的是一个数组 [状态值, 修改状态的方法];
//执行 setNum(value) => 修改num状态值为value => 通知视图更新;
useState实现原理:
// 数据的存储器
let hookState = [];
// 默认,从当前第一个开始保存
let hookIndex = 0;
/*
更新管理状态
*/
function useState(initalValue) {
// 判断是否是第一次初始化数据
if (typeof hookState[hookIndex] === 'undefined') {
if (typeof initalValue === 'function') {
//参数传递是函数
hookState[hookIndex] = initalValue();
} else {
//参数是其它类型
hookState[hookIndex] = initalValue;
}
}
// 保存一下索引值,以免后面修改引起数据冲突
let currentIndex = hookIndex;
function setState(value) {
// 判断修改内容与缓存的内容是否一致,优化性能;
if (Object.is(hookState[currentIndex], value)) return;
if (typeof value === 'function') {
// 判断是否是函数,函数就调用执行
hookState[currentIndex] = value(hookState[currentIndex]);
} else {
// 其它类型直接赋值
hookState[currentIndex] = value;
};
// 通知视图更新
flushQueue();
};
hookIndex++;
return [hookState[currentIndex], setState];
};
2. useEffect 函数组件生命周期
注意:
(1) useEffect必须在函数的最外层上下文调用,不能将其放在判断条件、循环语句中;
(2)useEffect如果设置返回值,则返回值必须是一个函数,代表销毁组件的触发,不能使用async修改传入的函数,因为async修饰的函数返回是一个promise实例,不符合要求!
let Demo = (props) => {
const [count, setCount] = useState(0)
// 这种是错误写法
if (a > 5) {
useEffect(async () => {
let ccc = await fn();
}, [])
}
useEffect(() => {
//页面请求数据的方法放这里
console.log('这是模拟componentDidMount钩子函数')
// return ()=> {
// 卸载或者更新数据需要清理上一次闭包中函数副作用的操作可以放这里
// console.log('这是模拟componentWillUnMount钩子函数')
// }
}, [count])
//第二个参数是一个数组,数组里的内容变化useEffect函数就会执行,
//如果不写默认会监听所有状态,空数组就会在初始化执行一次;
return (
<div>
<p>{count}</p>
</div>
)
}
useEffect实现原理:
useEffect与useLayoutEffect的区别:
useLayoutEffect会阻塞浏览器渲染真实dom,优先执行effect链表中的callback;
useEffect不会阻塞浏览器渲染真实dom,在渲染真实dom同时,去执行effect链表中的callback;
useLayoutEffect设置的callback要优先去useEffect设置的函数;
在两者设置的callback中都可以获取dom元素,原因是真实dom已经创建,区别只是浏览器是否渲染;
如果在callback函数中又修改了状态值(即调用了setNum方法,视图要跟新):
useEffect:浏览器肯定会把第一次真实dom渲染了再去渲染第二次真实dom;
useLayoutEffect:浏览器是把两次真实dom的渲染合并在一起渲染;
/*
useEffect数据存储列表
*/
// useEffect 存储列表
let hookEffect = [];
//useEffect 默认的索引值
let hookEffectIndex = 0;
/*
生命周期函数(页面渲染和useEffect队列一起执行,属于异步任务)
*/
function useEffect(callback, deps) {
// debugger;
// 判断第一个参数是否是函数
if (typeof callback !== 'function') {
throw Error('useEffect the first argument is not a function')
};
// 判断第二个参数是否是数组类型、undefined、null;
if (!(deps instanceof Array) && typeof deps !== 'undefined' && deps !== null) {
throw Error('useEffect the second argument is not a ' + typeof deps)
};
// 将销毁函数、依赖项都放入存储器中;
function setEffectData() {
// callback中是否有销毁函数,需要将callback至于页面渲染之后执行
let hookEffectItem = [undefined, deps];
setTimeout(() => {
hookEffectItem[0] = callback();
}, 0)
//将销毁函数destroy还有依赖项deps缓存起来
hookEffect[hookEffectIndex] = hookEffectItem;
hookEffectIndex++;
}
/* 初始化时候需要将销毁函数,依赖项缓存起来,下次执行时候先调用销毁函数 */
if (hookEffect[hookEffectIndex]) {
/* 更新数据时候获取缓存的数据 */
// lastDestroy 代表初始时候(更新时候上一次)的销毁函数,lastDeps代表依赖项;
let [lastDestroy, lastDeps] = hookEffect[hookEffectIndex];
// 是否有设置依赖项,默认是false 空数组是没有依赖项,null和undefined是所有依赖项;
let hasDep = lastDeps instanceof Array ? false : true;
if (lastDeps instanceof Array && lastDeps.length) {
// 判断依赖是否变化,如果依赖变化,当前的依赖deps与上一次的依赖lastDeps肯定不一样
// every循环 只有全部相同才会返回true, 只要有一项不同就返回 false;
hasDep = !(deps.every((item, idx) => item === lastDeps[idx]));
};
if (hasDep) {
// 如果有依赖且变化
lastDestroy && lastDestroy();
setEffectData();
} else {
// 没有依赖
hookEffectIndex++;
}
} else {
setEffectData();
}
};
3. useMemo 缓存计算属性,(useCallback 缓存函数)
缓存属性想必大家应该知道,缓存函数这里解释一下,由于函数组件每次更新都是产生一个新的作用域,函数组件里面的代码从上到下都会执行一遍,内部函数也都会创建到绑定的过程,为了性能的优化,对于需要父组件传递到子组件的函数方法,react提供了useCallback来缓存函数。
function Demo() {
const [num, setNum] = useState(20),
[count, setCount] = useState(30),
[msg, SetMsg] = useState('hello word');
/*
函数组件的每一次更新,都是把函数重新执行,产生一个新的闭包,内部的代码也要重新执行一次;
如果我们代码里面有需要复杂的逻辑需要计算,每次更新会消耗很多的性能,所以使用useMemo来缓存计算属性;
*/
// 不加useMemo之前每次更新数据都会执行下面的代码
// const allCount = num + count;
const allCount = useMemo(() => {
/*
如果只是简单的计算没必要缓存,因为缓存的也需要消耗内存,可能缓存的成本比重新渲染更高,例如
let number = 0;
number = numebr +1;
return number;
*/
let number = 0;
for(let i=0;i<100000;++i){
number = number +i-(number-i*1.1);
}
return number;
}, [/* 这里可以添加依赖,会根据依赖变化,函数内部代码重新执行 */])
// useCallback使用方法
const testfn = useCallback(() => {
// 内部的代码会根据依赖执行
let number = 0;
for(let i=0;i<100000;++i){
number = number +i-(number-i*1.1);
}
}, [/* 这里可以添加依赖,会根据依赖变化,函数内部代码重新执行 */])
function handle() {
SetMsg(msg);
}
return <>
<button onClick={handle}>点击</button>
{/* 这是子组件, useCallback用作传递给子组件的函数,
以免父组件修改内容,子组件没有修改,所以子组件也没必要跟新 */}
<Child callback={testfn} />
{allCount}
</>
}
useMemo与useCallback原理差不多,这儿就只写一个:
/*
useMemo与useCallback原理几乎相同,useMemo缓存计算属性,useCallback是缓存函数;
*/
// 存储useMemo列表
let hookMemo = [];
let hookMemoIndex = 0;
function useMemo(callback, deps) {
// 判断第一个参数是否是函数
if (typeof callback !== 'function') {
throw Error('useEffect the first argument is not a function')
};
// 判断第二个参数是否是数组类型、undefined、null;
if (!(deps instanceof Array) && typeof deps !== 'undefined' && deps !== null) {
throw Error('useEffect the second argument is not a ' + typeof deps)
};
// 将缓存数据、依赖项都放入存储器中;
function setMemoData() {
// callback函数有返回值就执行函数,没有就是undefined;
let value = callback && callback();
//将数据value还有依赖项deps缓存起来
hookMemo[hookMemoIndex] = [value, deps];
hookMemoIndex++;
return value;
}
if (hookMemo[hookMemoIndex]) {
//非首次渲染,选获取到之前存储的数据和依赖
const [lastValue, lastDeps] = hookMemo[hookMemoIndex];
// 是否有设置依赖项,默认是false 空数组是没有依赖项,null和undefined是所有依赖项;
let hasDep = lastDeps instanceof Array ? false : true;
if (lastDeps instanceof Array && lastDeps.length) {
// 判断依赖是否变化,如果依赖变化,当前的依赖deps与上一次的依赖lastDeps肯定不一样
// every循环 只有全部相同才会返回true, 只要有一项不同就返回 false;
hasDep = !(deps.every((item, idx) => item === lastDeps[idx]));
};
if (hasDep) {
// 如果有依赖且变化
return setMemoData();
} else {
// 没有依赖
hookMemoIndex++;
return lastValue;
}
} else {
//首次渲染
return setMemoData();
}
};
4. useReducer 单个组件状态管理,原理与redux差不多,但是redux是全局状态管理,useReducer 可以理解为是 useState 的升级版,都是用来存储和更新 state;
/*
适用场景:
1.管理多条数据或者需要处理复杂逻辑的时候:(分类);
比如 侧边栏的数据{menu: [], active: 0, id},且里面的数据需要切换多种状态可以用对象来管理;
2.useState时候尽量保证单个状态,在需要管理大量数据的场景中,使用useReducer更加合适;
*/
//定义reducer函数
const reducer = (state, action) => {
switch (action.type) {
case 'add':
return {count: state.count + 1};
case 'del':
return {count: state.count - 1};
default:
throw new Error();
}
};
function Demo() {
const [num, setNum] = useState(10);
//useReducer(callBack, initValue, init)接收三个参数;
// 1.第一个参数是逻辑处理的函数
// 2.初始数据值
// 3.更新初始数据的值
const [state, dispatch] = useReducer(reducer, {count: 0});
return <>
{num}
<button onClick={e => dispatch({type: 'add'})}>增加</button>
<button onClick={e => dispatch({type: 'del'})}>减少</button>
</>
}
useReducer原理:(有兴趣的伙伴可以尝试将useReducer与useState一起封装到一个函数中,通过传递不同的参数控制不同的功能)
/*
useReducer 单个组件的状态管理
*/
// useReducer 存储列表
// 数据的存储器
let hookReducer = [];
// 默认,从当前第一个开始保存
let hookReducerIndex = 0;
function useReducer(reducer, initalValue) {
// 判断是否是第一次初始化数据
if (typeof hookState[hookIndex] === 'undefined') {
if (typeof initalValue === 'function') {
//参数传递是函数
hookState[hookIndex] = initalValue();
} else {
//参数是其它类型
hookState[hookIndex] = initalValue;
}
}
// 保存一下索引值,以免后面修改引起数据冲突
let currentIndex = hookIndex;
function dispatch(action) {
// 判断修改内容与缓存的内容是否一致,优化性能;
if (Object.is(hookState[currentIndex], reducer(hookState[currentIndex], action))) return;
if (typeof reducer(action) === 'function') {
// 判断是否是函数,函数就调用执行
hookState[currentIndex] = reducer(hookState[currentIndex], action)();
} else {
// 其它类型直接赋值
hookState[currentIndex] = reducer(hookState[currentIndex], action);
};
// 通知视图更新
flushQueue();
};
hookIndex++;
return [hookState[currentIndex], dispatch];
}
5. useRef函数与useImperativeHandle函数
类组件中给某个子组件绑定ref可以直接获取到子组件中的实例对象,但是函数组件不行,必须要借助React.forwardRef函数与useImperativeHandle函数才行,如下所示:
/*
useImperativeHandle 传递ref实例数据
类组件中,父组件给子组件绑定ref可以直接拿到子组件实例,但是函数组件不行
需要借助React.forwardRef这个高阶函数包裹子组件函数,这样组件函数中就会
有props与ref两个参数,子组件可以使用ref绑定,父组件就可以获取子组件的元素,
内部使用useImperativeHandle(ref, callback)函数,callback返回的对象可以
添加子组件的数据,这样父组件就可以获取到子组件的内容;
*/
//这是父组件
const Parent = () => {
const boxRef = useRef(null); //null代表初始值,也可以不传
return (
<Child ref={boxRef} />
)
}
//这是子组件
const Child = React.forwardRef((props, ref) => {
const [msg, setMsg] = useState('你好世界');
useImperativeHandle(ref, () => {
// 使用useImperativeHandle导出一个对象,
// 父组件的box.current中就可以获取子组件的属性和方法
return {
msg
}
})
return (
// 如果直接在元素上面绑定ref,则父组件就获取子组件的元素上面的对象属性
<div ref={ref}>这是子组件</div>
)
})
useRef与useImperativeHandle原理:
/*
useRef 获取ref对象
*/
// 存储useRef列表
let hookRef = [];
let hookRefIndex = 0;
function useRef(initalValue) {
// 判断是否是第一次渲染
if (typeof hookState[hookIndex] === 'undefined') {
hookRef[hookRefIndex] = {current: initalValue};
};
let currentIndex = hookRefIndex;
hookRefIndex++;
return hookRef[currentIndex];
}
function useImperativeHandle(ref, callback) {
//callback返回一个对象,将callback返回的数据放在ref的current中;
ref.current = callback();
}
以上就是react函数式组件中比较重要的hooks函数,这篇文章希望可以帮助你理解它,在未来的开发中遇到问题也能及时解决,最后附上所有hooks函数源码,希望你们也能自己动手尝试的写一写。
import React from 'react';
import ReactDOM from 'react-dom/client';
/*
useState管理函数中数据的状态
*/
// 数据的存储器
let hookState = [];
// 默认,从当前第一个开始保存
let hookIndex = 0;
//任务队列更新的状态
let pedding = false;
/*
更新管理状态
*/
function useState(initalValue) {
// 判断是否是第一次初始化数据
if (typeof hookState[hookIndex] === 'undefined') {
if (typeof initalValue === 'function') {
//参数传递是函数
hookState[hookIndex] = initalValue();
} else {
//参数是其它类型
hookState[hookIndex] = initalValue;
}
}
// 保存一下索引值,以免后面修改引起数据冲突
let currentIndex = hookIndex;
function setState(value) {
// 判断修改内容与缓存的内容是否一致,优化性能;
if (Object.is(hookState[currentIndex], value)) return;
if (typeof value === 'function') {
// 判断是否是函数,函数就调用执行
hookState[currentIndex] = value(hookState[currentIndex]);
} else {
// 其它类型直接赋值
hookState[currentIndex] = value;
};
// 通知视图更新
flushQueue();
};
hookIndex++;
return [hookState[currentIndex], setState];
};
/*
useReducer 单个组件的状态管理
*/
// useReducer 存储列表
// 数据的存储器
let hookReducer = [];
// 默认,从当前第一个开始保存
let hookReducerIndex = 0;
function useReducer(reducer, initalValue) {
// 判断是否是第一次初始化数据
if (typeof hookState[hookIndex] === 'undefined') {
if (typeof initalValue === 'function') {
//参数传递是函数
hookState[hookIndex] = initalValue();
} else {
//参数是其它类型
hookState[hookIndex] = initalValue;
}
}
// 保存一下索引值,以免后面修改引起数据冲突
let currentIndex = hookIndex;
function dispatch(action) {
// 判断修改内容与缓存的内容是否一致,优化性能;
if (Object.is(hookState[currentIndex], reducer(hookState[currentIndex], action))) return;
if (typeof reducer(action) === 'function') {
// 判断是否是函数,函数就调用执行
hookState[currentIndex] = reducer(hookState[currentIndex], action)();
} else {
// 其它类型直接赋值
hookState[currentIndex] = reducer(hookState[currentIndex], action);
};
// 通知视图更新
flushQueue();
};
hookIndex++;
return [hookState[currentIndex], dispatch];
}
/*
useEffect数据存储列表
*/
// useEffect 存储列表
let hookEffect = [];
//useEffect 默认的索引值
let hookEffectIndex = 0;
/*
生命周期函数(页面渲染和useEffect队列一起执行,属于异步任务)
*/
function useEffect(callback, deps) {
// debugger;
// 判断第一个参数是否是函数
if (typeof callback !== 'function') {
throw Error('useEffect the first argument is not a function')
};
// 判断第二个参数是否是数组类型、undefined、null;
if (!(deps instanceof Array) && typeof deps !== 'undefined' && deps !== null) {
throw Error('useEffect the second argument is not a ' + typeof deps)
};
// 将销毁函数、依赖项都放入存储器中;
function setEffectData() {
// callback中是否有销毁函数,需要将callback至于页面渲染之后执行
let hookEffectItem = [undefined, deps];
setTimeout(() => {
hookEffectItem[0] = callback();
}, 0)
//将销毁函数destroy还有依赖项deps缓存起来
hookEffect[hookEffectIndex] = hookEffectItem;
hookEffectIndex++;
}
/* 初始化时候需要将销毁函数,依赖项缓存起来,下次执行时候先调用销毁函数 */
if (hookEffect[hookEffectIndex]) {
/* 更新数据时候获取缓存的数据 */
// lastDestroy 代表初始时候(更新时候上一次)的销毁函数,lastDeps代表依赖项;
let [lastDestroy, lastDeps] = hookEffect[hookEffectIndex];
// 是否有设置依赖项,默认是false 空数组是没有依赖项,null和undefined是所有依赖项;
let hasDep = lastDeps instanceof Array ? false : true;
if (lastDeps instanceof Array && lastDeps.length) {
// 判断依赖是否变化,如果依赖变化,当前的依赖deps与上一次的依赖lastDeps肯定不一样
// every循环 只有全部相同才会返回true, 只要有一项不同就返回 false;
hasDep = !(deps.every((item, idx) => item === lastDeps[idx]));
};
if (hasDep) {
// 如果有依赖且变化
lastDestroy && lastDestroy();
setEffectData();
} else {
// 没有依赖
hookEffectIndex++;
}
} else {
setEffectData();
}
};
/*
useLayoutEffect数据存储列表
*/
// useLayoutEffect 存储列表
let hookLayoutEffect = [];
//useLayoutEffect 默认的索引值
let hookLayoutEffectIndex = 0;
/*
生命周期函数(先执行useLayoutEffect队列再执行页面渲染,属于同步任务)
但是useLayoutEffect的队列中已经可以获取到真实dom,所以在更新数据中与执行顺序还是在最后;
*/
function useLayoutEffect(callback, deps) {
// debugger;
// 判断第一个参数是否是函数
if (typeof callback !== 'function') {
throw Error('useEffect the first argument is not a function')
};
// 判断第二个参数是否是数组类型、undefined、null;
if (!(deps instanceof Array) && typeof deps !== 'undefined' && deps !== null) {
throw Error('useEffect the second argument is not a ' + typeof deps)
};
// 将销毁函数、依赖项都放入存储器中;
function setEffectData() {
let hookLayoutEffectItem = [undefined, deps];
// queueMicrotask微任务,模拟与useRef使用能够获取到dom元素
queueMicrotask(() => {
// callback中是否有销毁函数
hookLayoutEffectItem[0] = callback();
})
//将销毁函数destroy还有依赖项deps缓存起来
hookLayoutEffect[hookLayoutEffectIndex] = hookLayoutEffectItem;
hookLayoutEffectIndex++;
}
/* 初始化时候需要将销毁函数,依赖项缓存起来,下次执行时候先调用销毁函数 */
if (hookLayoutEffect[hookLayoutEffectIndex]) {
/* 更新数据时候获取缓存的数据 */
// lastDestroy 代表初始时候(更新时候上一次)的销毁函数,lastDeps代表依赖项;
let [lastDestroy, lastDeps] = hookLayoutEffect[hookLayoutEffectIndex];
// 是否有设置依赖项,默认是false 空数组是没有依赖项,null和undefined是所有依赖项;
let hasDep = lastDeps instanceof Array ? false : true;
if (lastDeps instanceof Array && lastDeps.length) {
// 判断依赖是否变化,如果依赖变化,当前的依赖deps与上一次的依赖lastDeps肯定不一样
// every循环 只有全部相同才会返回true, 只要有一项不同就返回 false;
hasDep = !(deps.every((item, idx) => item === lastDeps[idx]));
};
if (hasDep) {
// 如果有依赖且变化
lastDestroy && lastDestroy();
setEffectData();
} else {
// 没有依赖
hookLayoutEffectIndex++;
}
} else {
setEffectData();
}
};
/*
useMemo与useCallback原理几乎相同,useMemo缓存计算属性,useCallback是缓存函数;
*/
// 存储useMemo列表
let hookMemo = [];
let hookMemoIndex = 0;
function useMemo(callback, deps) {
// 判断第一个参数是否是函数
if (typeof callback !== 'function') {
throw Error('useEffect the first argument is not a function')
};
// 判断第二个参数是否是数组类型、undefined、null;
if (!(deps instanceof Array) && typeof deps !== 'undefined' && deps !== null) {
throw Error('useEffect the second argument is not a ' + typeof deps)
};
// 将缓存数据、依赖项都放入存储器中;
function setMemoData() {
// callback函数有返回值就执行函数,没有就是undefined;
let value = callback && callback();
//将数据value还有依赖项deps缓存起来
hookMemo[hookMemoIndex] = [value, deps];
hookMemoIndex++;
return value;
}
if (hookMemo[hookMemoIndex]) {
//非首次渲染,选获取到之前存储的数据和依赖
const [lastValue, lastDeps] = hookMemo[hookMemoIndex];
// 是否有设置依赖项,默认是false 空数组是没有依赖项,null和undefined是所有依赖项;
let hasDep = lastDeps instanceof Array ? false : true;
if (lastDeps instanceof Array && lastDeps.length) {
// 判断依赖是否变化,如果依赖变化,当前的依赖deps与上一次的依赖lastDeps肯定不一样
// every循环 只有全部相同才会返回true, 只要有一项不同就返回 false;
hasDep = !(deps.every((item, idx) => item === lastDeps[idx]));
};
if (hasDep) {
// 如果有依赖且变化
return setMemoData();
} else {
// 没有依赖
hookMemoIndex++;
return lastValue;
}
} else {
//首次渲染
return setMemoData();
}
};
/*
useContext 获取上下文对象
*/
function useContext(context) {
// 使用createContext的时候会创建一个上下文,内部使用provider组件可以将数据放在value上挂载到context上面
// 里面的_currentValue就是value挂载的数据;
return context._currentValue;
};
/*
useRef 获取ref对象
*/
// 存储useRef列表
let hookRef = [];
let hookRefIndex = 0;
function useRef(initalValue) {
// 判断是否是第一次渲染
if (typeof hookState[hookIndex] === 'undefined') {
hookRef[hookRefIndex] = {current: initalValue};
};
let currentIndex = hookRefIndex;
hookRefIndex++;
return hookRef[currentIndex];
}
function useImperativeHandle(ref, callback) {
//callback返回一个对象,将callback返回的数据放在ref的current中;
ref.current = callback();
}
// 更新队列批处理;
function flushQueue() {
if (!pedding) {
pedding = true;
Promise.resolve().then(() => {
// 视图更新函数
render();
// 更新数据之前将索引恢复默认
hookIndex = 0;
hookReducerIndex = 0;
hookEffectIndex = 0;
hookLayoutEffectIndex = 0;
hookMemoIndex = 0;
hookRefIndex = 0;
// 恢复更新的状态
pedding = false
})
}
};
function App() {
const [msg, setMsg] = useState('hello world'),
[count, setCount] = useState(0);
function handle() {
setCount(count + 1);
}
function edit() {
setMsg(Math.random().toFixed(2));
}
const allCount = useMemo(() => {
console.log('count=======', count)
return count;
}, [])
useEffect(() => {
console.log('11111111');
return () => {
console.log('销毁了')
}
}, [count])
return <div style={{textAlign: 'center'}}>
<p>count========={count}</p>
<p>msg========={msg}</p>
<p>allcount========={allCount}</p>
<button onClick={handle}>点击修改count</button>
<button onClick={edit}>点击修改msg</button>
</div>
}
const root = ReactDOM.createRoot(document.getElementById('root'));
function render() {
root.render(
<App />
);
};
render();