如何使 React 中的 useEffect、useLayoutEffect 只调用一次
Hook 是 React 16.8 的新增特性,旨在替代原来 React 中的 Class 组件。React 官方已经对使用 Class 组件采取了负面态度(见此处)。虽然目前官方仍然明确声明“没有计划从 React 中移除 class
”(见此处),但待 Hook 成熟之后,Class 组件语法被废弃恐怕是迟早的事情。
useEffect
、useLayoutEffect
可用于替代 Class 组件生命周期的方法。通常,它们都会反复执行。有时候,这会导致死循环。
使 useEffect
、useLayoutEffect
只调用一次的方法是,向它们传入第二个空数组参数。如:
useEffect(() => init(), []);
【踩坑提醒】
不要使用如下途径来使 useEffect
、useLayoutEffect
只调用一次。
let didInit = false;
useEffect(() => {
if (!didInit) {
didInit = true; // didInit 放在哪里都不会起作用
init();
didInit = true; // didInit 放在哪里都不会起作用
}
});
有过多线程并发编程经验的人应该知道,上面的 didInit
将不起作用,且程序可能将在初始化时陷入死循环。虽然客户端 JavaScript 是单线程的,但由于 JavaScript 有异步语法,上述代码仍然会造成对 didInit 的异步并发访问,导致 didInit
不起作用。由于 useEffect 可以被反复触发,如果上述的函数 init()
进行了对 state 的异步更新,这将导致对 useEffect 的持续循环触发。
由于 JavaScript 不支持多线程锁,所以它无法使用 双重检查锁定
。因此,在 React 中,不要自作聪明编写这种坏代码。
如果想知道更多关于从 Class 组件到 Hook 的重构方法,可见:https://react.docschina.org/docs/hooks-faq.html#from-classes-to-hooks