react hook面世已经有一段时间了,相信很多人都已经在代码中用上了hooks。而对于 useEffect 和 useLayoutEffect,我们使用的最多的应该就是useEffect。那他们两个到底有什么不一样的地方?
使用方式
这两个函数的使用方式其实非常简单,他们都接受一个函数一个数组,只有在数组里面的值改变的情况下才会再次执行 effect。所以对于使用方式我就不过多介绍了,不清楚的可以先参考官网。
差异
useEffect 是异步执行的,而useLayoutEffect是同步执行的。
useEffect 的执行时机是浏览器完成渲染之后,而 useLayoutEffect 的执行时机是浏览器把内容真正渲染到界面之前,和 componentDidMount 等价。
具体表现
我们用一个很简单的例子
import React, { useEffect, useLayoutEffect, useState } from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
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");
// }, []);
return (
<>
<div>{state}</div>
</>
);
}
export default App;
因为 useEffect 是渲染完之后异步执行的,所以会导致 hello world 先被渲染到了屏幕上,再变成 world hello,就会出现闪烁现象。而 useLayoutEffect 是渲染之前同步执行的,所以会等它执行完再渲染上去,就避免了闪烁现象。也就是说我们最好把操作 dom 的相关操作放到 useLayouteEffect 中去,避免导致闪烁。
ssr
也正是因为 useLayoutEffect 可能会导致渲染结果不一样的关系,如果你在 ssr 的时候使用这个函数会有一个 warning。
Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format. This will lead to a mismatch between the initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client. See https://fb.me/react-uselayouteffect-ssr for common fixes.
这是因为 useLayoutEffect 是不会在服务端执行的,所以就有可能导致 ssr 渲染出来的内容和实际的首屏内容并不一致。而解决这个问题也很简单:
放弃使用 useLayoutEffect,使用 useEffect 代替
如果你明确知道 useLayouteffect 对于首屏渲染并没有影响,但是后续会需要,你可以这样写
import { useEffect, useLayoutEffect } from 'react';
export const useCustomLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
当你使用 useLayoutEffect 的时候就用 useCustomLayoutEffect 代替。这样在服务端就会用 useEffect ,这样就不会报 warning 了。