Mal*_*hak 5 javascript typescript reactjs next.js
我知道 React 在严格模式下调用 useEffect 两次,这个问题是关于询问正确的处理方法是什么。
\n我最近遇到了一个问题,React useEffect在strictMode. 我想保持严格模式以避免出现问题,但我没有看到任何好的方法来确保某些特定效果仅运行一次。这是在一个next.js环境中,我特别希望代码只在客户端中运行一次。
例如,给定以下组件:
\nimport React, { useState, useEffect } from "react";\nimport ky from "ky";\n\nconst NeedsToRunOnce: React.FC = () => {\n const [loading, setLoading] = useState(false);\n\n const doSomethingThatOnlyShouldHappenOnce = (data: any) => {\n // Do something with the loaded data that should only happen once\n console.log(`This log appears twice!`, data);\n };\n\n useEffect(() => {\n if (loading) return;\n console.log("This happens twice despite trying to set loading to true");\n setLoading(true);\n const fetchData = async () => {\n const data = await ky.get("/data/json_data_2000.json").json();\n setLoading(false);\n doSomethingThatOnlyShouldHappenOnce(data);\n };\n fetchData();\n }, []);\n return <div></div>;\n};\n\nexport default NeedsToRunOnce;\nRun Code Online (Sandbox Code Playgroud)\nuseEffect会被调用两次,并且两次loading仍然是 false。这是因为strictMode使得它在相同的状态下被调用两次。该请求将经过两次,并调用doSomethingThatOnlyShouldHappenOnce两次。上述所有console.log调用都会在控制台中出现两次。
由于我无法修改状态以让我的组件知道请求已经开始发生,因此如何阻止 get 请求发生两次,然后调用我只想调用一次的代码?(对于上下文,我正在使用 中加载的数据初始化外部库useEffect,并且该库应该只初始化一次)。
我发现了一个关于此问题的 GitHub 问题,其中 Dan Abramov 说道:
\n\n\n通常,您\xe2\x80\x99d 希望对您的效果进行某种清理。它应该取消您的获取,或者通过在效果中设置变量来确保您忽略其结果。一旦执行此操作,行为上就不会有实际差异。
\n
\n\n如果您取消有效清理中的提取,则开发中只会完成一个请求。然而,如果另一个请求触发,\xe2\x80\x99 也没关系。它\xe2\x80\x99无论如何都会被忽略,并且压力测试仅发生在开发中。
\n
虽然我原则上同意,但实际上这很乏味。我已经在我的应用程序中遇到了两个不同的用例,其中我有一些库调用或请求只需要在客户端中完成一次。
\n有什么可靠的方法可以确保其中的一段代码useEffect只在这里运行一次?使用状态来跟踪它已经运行并没有帮助,因为react连续两次使用相同的状态调用该组件。
小智 3
您可以使用 ref 来执行 useEffect 一次(第一次或第二次,如您所愿),或者只使用 customHook。就我而言,我第二次执行 useEffect。交换 true 和 false,第一次执行而不是第二次执行。
import { useEffect, useRef } from "react";
export default function useEffectOnce(fn: () => void) {
const ref = useRef(false);
useEffect(() => {
if (ref.current) {
fn();
}
return () => {
ref.current = true;
};
}, [fn]);
}
Run Code Online (Sandbox Code Playgroud)
要使用它,您可以在 param 中传递回调函数:
useEffectOnce(() => console.log("hello"));
Run Code Online (Sandbox Code Playgroud)