Heim > Fragen und Antworten > Hauptteil
Ich habe einen Zähler und useEffect
中的 console.log()
来记录我的状态中的每个更改,但是 useEffect
wird beim Montieren zweimal aufgerufen. Ich verwende React 18. Hier ist die CodeSandbox für mein Projekt und der Code unten:
import { useState, useEffect } from "react"; const Counter = () => { const [count, setCount] = useState(5); useEffect(() => { console.log("rendered", count); }, [count]); return ( <div> <h1> Counter </h1> <div> {count} </div> <button onClick={() => setCount(count + 1)}> click to increase </button> </div> ); }; export default Counter;
P粉1565327062023-10-20 11:55:03
更新:回顾一下这篇文章,稍微明智一点,请不要这样做。
使用ref
或创建一个没有的自定义hook
。
export const useClassicEffect = createClassicEffectHook(); function createClassicEffectHook() { if (import.meta.env.PROD) return React.useEffect; return (effect: React.EffectCallback, deps?: React.DependencyList) => { React.useEffect(() => { let isMounted = true; let unmount: void | (() => void); queueMicrotask(() => { if (isMounted) unmount = effect(); }); return () => { isMounted = false; unmount?.(); }; }, deps); }; }
P粉4594409912023-10-20 09:39:10
自 React 18 起,当您使用 StrictMode
进行开发
时,
useEffect
在挂载时被调用两次是正常的。以下是他们在 文档:
这看起来很奇怪,但最终,我们通过缓存 HTTP 请求并在有两个调用时使用清理函数来编写更好的 React 代码,无错误,符合当前指南,并与未来版本兼容一个问题。这是一个例子:
/* Having a setInterval inside an useEffect: */
import { useEffect, useState } from "react";
const Counter = () => {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => setCount((count) => count + 1), 1000);
/*
Make sure I clear the interval when the component is unmounted,
otherwise, I get weird behavior with StrictMode,
helps prevent memory leak issues.
*/
return () => clearInterval(id);
}, []);
return {count};
};
export default Counter;
在这篇非常详细的文章中,React 团队解释了 useEffect
前所未有并举例说明:
对于您的特定用例,您可以保持原样,无需担心。并且您不应该尝试将这些技术与 useEffect
中的 useRef
和 if
语句一起使用以使其触发一次,或删除 StrictMode
,因为正如您可以在 文档:
/* As a second example, an API call inside an useEffect with fetch: */
useEffect(() => {
const abortController = new AbortController();
const fetchUser = async () => {
try {
const res = await fetch("/api/user/", {
signal: abortController.signal,
});
const data = await res.json();
} catch (error) {
// ℹ️: The error name is "CanceledError" for Axios.
if (error.name !== "AbortError") {
/* Logic for non-aborted error handling goes here. */
}
}
};
fetchUser();
/*
Abort the request as it isn't needed anymore, the component being
unmounted. It helps avoid, among other things, the well-known "can't
perform a React state update on an unmounted component" warning.
*/
return () => abortController.abort();
}, []);
function TodoList() { const todos = useSomeDataFetchingLibraryWithCache(`/api/user/${userId}/todos`); // ...
如果您仍然遇到问题,也许您正在使用 useEffect
,正如他们在 不是效果:初始化应用程序 和 不是效果:购买产品,我建议您阅读文章作为一个整体。