import { useEffect, useLayoutEffect, useState } from "react";
import "./styles.css";
export default function App() {
const [color, setColor] = useState("red");
useLayoutEffect(() => {
setColor("green");
}, []);
useEffect(() => {
setColor("blue");
}, []);
return (
<div className="App">
<h1
style={{
color,
transition: "color 300ms ease-in-out"
}}
>
Hello World
</h1>
</div>
);
}
如题,这段代码为什么没有transition?
首次渲染时在commit阶段useLayoutEffect的create函数(也就是他的第一个参数)会被同步执行,由于其中使用色
setColor
,他会重新调度一次performSyncWorkOnRoot
被调度的performSyncWorkOnRoot
会在该轮更新完毕后被执行,同时在commit阶段中useEffect
的create函数会被当作一个宏任务调度,你可以理解为useEffect
的create函数会被setTimeout
延时调用该轮更新完毕后,开始执行前面被调度的
performSyncWorkOnRoot
开始新一轮的更新,在开始执行的过程中发现前一轮更新还有遗留的useEffect
需要执行,在这里就不会等setTimeout(实际上使用的是postMessage延时调用的)去执行他了,而是把他同步执行了,由于在useEffect
中使用了setColor("blue")
所以又会重新调度一次performSyncWorkOnRoot
,但是这一次调度有所不同,因为已经存在一个useLayoutEffect调度的任务而且他们的优先级还是一样的,所以不会再重复调度了,而是复用之前的任务,而由于每次setColor
时都会将update加到更新链表上,所以此时的更新链表就为
green ---> blue
待会在updateReducer中他们会被reduce成blue
所以虽然执行了两次setColor
,但是到最后只设置了blue颜色,我们在组件里打印一下color就能清除的看到这个过程打印结果
知道了原理之后要想出现transition效果就很简单了,只要上useEffect中调度的任务不复用useLayoutEffect调度的任务就行了所以我们可以把代码改成这样,就能出现transition效果了
此时的打印结果