1. useState 是 同步还是异步?
- 合成事件-- react 为了解决平台兼容性,封装了一套自己的事件机制,代理了浏览器原生事件,像onCLick,onChange等等。zh-hans.legacy.reactjs.org/docs/events…
- 批量处理
- isBatchingUpdates 、bachedUpdates函数
setCount(count=>count+1)
setCount(count=>count+1)
结果依赖第二次,只触发一次更新。
setTimer(123);
涉及多个变量变更,只触发一次更新, 可以减少渲染次数
- 关闭批量处理
https://react.nodejs.cn/reference/react-dom/flushSync
flushSync(() => {
setSomething(123);
});//react 18提供的函数,可以强制更新 --影响性能
- react18之前
- 在合成事件和生命周期方法中,setState是异步的。在浏览器原生DOM事件、setTimeout、promise中setState是异步的。
- react setState的实现中,会根据一个 isBatchingUpdates(是否批处理)变量判断是直接更新state还是放到队列中,直接更新就是同步的,默认是false,即同步。而react 在调用处理函数之前会调用一个bachedUpdates函数,将isBatchingUpdates改为true,即异步更新。
- 在react 18之后
- 任何情况都会自动执行批处理,多次更新合并为一次更新
2.useEffect 和 useLayoutEffect区别
useEffect是异步执行的,而useLayoutEffect是同步执行的。useEffect的执行时机是浏览器完成渲染之后,而useLayoutEffect的执行时机是浏览器把内容真正渲染到界面之前,和componentDidMount等价。- zhuanlan.zhihu.com/p/348701319
import React ,{useState,useEffect,useLayoutEffect} from "react";
export default function Layout(){
const [state, setState] = useState("hello world")
// useEffect(() => {
// let i = 0;
// while(i <= 100000000) {
// i++;
// };
// setState("world hello");
// }, []);
useLayoutEffect(() => {
let i = 0;
while(i <= 100000000) {
i++;
};
setState("world hello");
}, []);
useEffect(() => {
let i = 0;
while(i <= 100000000) {
i++;
};
setState("world hello");
}, []);
return (<>
<div>{state}</div>
</>)
}
- useEffect在渲染之后,异步执行,所以可能有闪烁,useLayout在渲染之前同步执行,执行完之后再进行渲染,避免了闪烁情况。
- useLayoutEffect不会在服务端执行,可能会导致SSR渲染出来的的内容与实际首屏不一致,避免在SSR服务器端使用,可以通过判断window对象是否存在来选择使用useState、useEffect
3. useRef
2. useRef.current = setTimeout等等,用于保存常量,常见的倒计时,在xiao hui shi
useRef 更新,不会触发重新渲染操作。
import React, { useRef } from "react";
export default function Reff() {
let count = useRef(1);
console.log("render");
return (
<>
<div>{count.current}</div>
<button
onClick={() => {
count.current = 2;
console.log("count.current===", count.current);
}}
>
useREF
</button>
</>
);
}
4. useMemo
用于缓存计算属性,一些在渲染过程中就有返回值的函数,以一个值为目标的函数等。 埋点、用户统计等初始化时需要保存的数据,可以用useMemo进行缓存。
- 如下,func 的值一直没有变过,返回值也没有改变,而每次刷新会重新执行func() 额外的性能开销,类似于这种我们可以用useMemo实现,这样返回值只有在num更新时才会重新执行
import React, { useState, useMemo } from "react";
export default function MeMMM() {
console.log("render");
let [count, setCount] = useState(0);
let [num, setNum] = useState(0);
// const func = () => {
// console.log("渲染时打印");
// return "str";
// };
const func = useMemo(() => {
console.log("useMemo包裹执行");
return "str2222222" + num;
}, [num]);
return (
<>
<div>{count}</div>
<button
onClick={() => {
setCount((count) => count + 1);
}}
>
渲染+++
</button>
<div>{func}</div>
<button
onClick={() => {
setNum((num) => num + 1);
}}
>
setNum++
</button>
</>
);
}

useMemo 消耗缓存,不要经常食用
- 使用场景1 缓存计算属性
- 使用场景2 当子组件依赖副组件某一个计算属性/对象/函数 并且子组件使用了React.memo进行包裹时。
如下,如果传递一个计算属性,由于引用地址改变,memo也会触发重新渲染
import React, { useState, useMemo, memo } from "react";
const Children1 = memo(({ func }) => {
console.log("这个打印触发了证明子组件重新渲染了");
return (
<>
<div>count:{func}</div>
</>
);
});
export default function MeMMM() {
console.log("render");
let [count, setCount] = useState(0);
let [num, setNum] = useState(0);
// const func = () => {
// console.log("渲染时打印");
// return "str";
// };
const func = useMemo(() => {
console.log("useMemo包裹执行");
return "str2222222" + num;
}, [num]);
return (
<>
<div>{count}</div>
<button
onClick={() => {
setCount((count) => count + 1);
}}
>
渲染+++
</button>
{/* <div>{func}</div> */}
<button
onClick={() => {
setNum((num) => num + 1);
}}
>
setNum++
</button>
<Children1 func={func} />
</>
);
}
5. CallBACK
-
当传递给子组件,子组件通过memo包裹了,接收一个方法,这个方法不变或者仅依赖某个值改变,当组件其他字段更新触发渲染时,这个函数会被重新创建,这样会导致他的内存地址会改变,这样会触发子组件重新刷新。可以用Callback包裹。 -
callback能力可以用useMemo实现 -
看如下返回值,两者实现能力是一样的
import React, { useState, useCallback, useMemo, memo } from "react";
const Child = memo(function ({ func }) {
console.log("Child 重新渲染");
return <div>123</div>;
});
export default function CallBACK1() {
let [count, setCount] = useState(0);
let [num, setNum] = useState(0);
// function test() {
// console.log("test");
// }
const test = useCallback(() => {
console.log("test");
}, [num]);
const test2 = useMemo(() => {
return () => {
console.log("test");
};
}, [num]);
return (
<>
<div>count:{count}</div>
<button
onClick={() => {
setCount((count) => count + 1);
}}
>
++++setCount+++++++
</button>
<div>Num:{num}</div>
<button
onClick={() => {
setNum((num) => num + 1);
}}
>
++++num+++++++
</button>
<Child func={test} />
</>
);
}
///shouldComponentUpdate