如何有效处理React中useEffect运行两次的问题?
P粉759457420
P粉759457420 2023-10-20 00:00:23
0
2
802

我有一个计数器和 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粉759457420
P粉759457420

全部回复(2)
P粉156532706

更新:回顾一下这篇文章,稍微明智一点,请不要这样做。

使用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粉459440991

自 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 中的 useRefif 语句一起使用以使其触发一次,或删除 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">不是效果:初始化应用程序 不是效果:购买产品,我建议您阅读文章作为一个整体。

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板