我有一個計數器和useEffect
中的console.log()
來記錄我的狀態中的每個更改,但是useEffect
在掛載時被調用兩次。我正在使用 React 18。這是我的專案的 CodeSandbox 和下面的程式碼:
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
,正如他們在useEffect /learn/synchronizing-with-effects#not-an-effect-initializing-the-application" rel="noreferrer">不是效果:初始化應用程式 和不是效果:購買產品,我建議您閱讀文章作為一個整體。