【发布时间】:2022-07-03 09:01:38
【问题描述】:
我最近遇到了一个问题,React useEffect 在 strictMode 中被调用了两次。我想保持严格模式以避免出现问题,但我没有看到任何确保某些特定效果只运行一次的好方法。这是在next.js 环境中,我特别希望代码只在客户端运行一次。
例如,给定以下组件:
import React, { useState, useEffect } from "react";
import ky from "ky";
const NeedsToRunOnce: React.FC = () => {
const [loading, setLoading] = useState(false);
const doSomethingThatOnlyShouldHappenOnce = (data: any) => {
// Do something with the loaded data that should only happen once
console.log(`This log appears twice!`, data);
};
useEffect(() => {
if (loading) return;
console.log("This happens twice despite trying to set loading to true");
setLoading(true);
const fetchData = async () => {
const data = await ky.get("/data/json_data_2000.json").json();
setLoading(false);
doSomethingThatOnlyShouldHappenOnce(data);
};
fetchData();
}, []);
return <div></div>;
};
export default NeedsToRunOnce;
useEffect 将被调用两次,而两次loading 仍然为假。这是因为strictMode 使它以相同的状态被调用两次。该请求将通过两次,并调用doSomethingThatOnlyShouldHappenOnce 两次。上面的所有console.log 调用都会在控制台中出现两次。
由于我无法修改状态以让我的组件知道请求已经开始发生,我如何才能阻止 get 请求发生两次然后调用我仅想要的代码被调用一次? (就上下文而言,我正在使用useEffect 中加载的数据初始化一个外部库,并且该库应该只初始化一次)。
我找到了一个GitHub issue about this Dan Abramov 说:
通常您会希望对效果进行某种清理。它应该要么取消你的获取,要么通过在你的效果中设置一个变量来确保你忽略它的结果。完成此操作后,行为应该没有实际差异。
如果您取消 fetch in effect 清理,则只有一个请求将在开发中完成。但是,是否触发另一个请求也无关紧要。无论如何它都被忽略了,压力测试只发生在开发中。
虽然我原则上同意,但在实践中这很乏味。我已经在我的应用程序中遇到了两个不同的用例,其中我有一些只需要在客户端中完成一次的库调用或请求。
确保useEffect 中的一段代码仅在此处运行一次的可靠方法是什么?使用状态来跟踪它已经运行并没有帮助,因为react 连续两次以相同的状态调用该组件。
【问题讨论】:
-
你的 effect 的依赖数组是空的,但是你的 effect 读取的是
loading,所以它也必须依赖它。
标签: javascript reactjs typescript next.js