首頁  >  問答  >  主體

如何有效處理React中useEffect執行兩次的問題?

我有一個計數器和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粉759457420P粉759457420390 天前781

全部回覆(2)我來回復

  • P粉156532706

    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);
      };
    }

    回覆
    0
  • P粉459440991

    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">不是效果:初始化應用程式不是效果:購買產品,我建議您閱讀文章作為一個整體。

    回覆
    0
  • 取消回覆