一、useEffect
它跟class组件中的componentDidMount、componentDidUpdate和component WillUnMount具有相同的用途,只不过被合并成了一个API。
const App = () => {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = count;
});
return (
<div
onClick={() => {
setCount(count + 1);
}}
>
Click(count:{count})
</div>
);
};
二、useLayoutEffect
其与useEffect函数相同,但它内部的更新计划会在浏览器执行绘制之前同步刷新。
demo1
const App = () => {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`useEffect - count=${count}`)
const pre = Date.now();
while(Date.now() - pre < 500) {}
// count为0时重新生成个随机数
if (count === 0) {
setCount(10 + Math.random() * 200);
}
}, [count]);
console.log('render');
return (
<div onClick={() => setCount(0)}>{count}</div>
);
}
分别用useEffect、useLayoutEffect运行,发现当我们点击元素时前者页面会更新为0后,立即更新为随机数;后者页面不会更新为0,而是等待useLayoutEffect内部状态修改后,才去更新页面。
使用useLayoutEffect函数的页面更新的比较卡顿,因为useLayoutEffect会阻塞浏览器的渲染。所以我们尽可能使用标准的 useEffect 以避免阻塞视觉更新。
demo2
const Animation = () => {
let red = useRef();
let green = useRef();
let style = {width: '100px', height: '100px'};
useEffect(() => {
red.current.style.transform = 'translate(500px)';
red.current.style.transition = 'all 500ms';
})
useLayoutEffect(() => {
green.current.style.transform = 'translate(500px)';
green.current.style.transition = 'all 500ms';
})
return (
<div>
<div style={{...style, background: 'red'}} ref={red}></div>
<div style={{...style, background: 'green'}} ref={green}></div>
</div>
)
}
const App = () => {
return (
<div>
<Animation />
</div>
);
};
小结
除非要修改DOM并且不让用户看到修改DOM的过程,才考虑使用useLayoutEffect,否则应当使用useEffect。