You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Header组件开发遇Next.js警告:Maximum update depth exceeded

解决React组件中"Maximum update depth exceeded"警告

问题根源

你的代码里,Header组件内的useEffect没有配置依赖数组,这会导致这个effect在组件每次渲染时都被触发。而effect内部调用setUser会触发组件重新渲染,进而再次执行这个effect,形成无限循环,最终触发更新深度超限的警告。

自定义hookuseOutsideAlerter中的useEffect是正常的,它的空依赖数组[]确保只在hook初始化时执行一次,不会引发循环问题。

修复方案

给Header里的useEffect添加空依赖数组[],让它仅在组件挂载时执行一次,读取localStorage中的用户信息:

useEffect(() => {
  const user = localStorage.getItem("user");
  if (user) {
    setUser(JSON.parse(user));
  }
}, []); // 这里添加空依赖数组

可选优化:可以额外判断当前user状态是否为null,避免不必要的状态更新:

useEffect(() => {
  if (!user) { // 仅当user为空时才读取localStorage
    const storedUser = localStorage.getItem("user");
    if (storedUser) {
      setUser(JSON.parse(storedUser));
    }
  }
}, [user]); // 此时依赖数组为[user],仅当user变化时执行

修正后的完整代码

import React from "react";
import { useState, useEffect, useRef } from "react";
import { FaChevronDown, FaLastfmSquare } from "react-icons/all";
import { signOut } from "firebase/auth";
import { useRouter } from "next/router";
import { auth } from "../utils/auth";

function useOutsideAlerter() {
  const [visible, setVisible] = useState(false);
  const ref = useRef(null);

  useEffect(() => {
    const handleHideDropdown = (event) => {
      if (event.key === "Escape") {
        setVisible(false);
      }
    };

    const handleClickOutside = (e) => {
      if (ref.current && !ref.current.contains(e.target)) {
        setVisible(false);
      }
    };

    document.addEventListener("keydown", handleHideDropdown, true);
    document.addEventListener("mousedown", handleClickOutside, true);

    return () => {
      document.removeEventListener("keydown", handleHideDropdown, true);
      document.removeEventListener("mousedown", handleClickOutside, true);
    };
  }, []);

  return { visible, ref, setVisible };
}

const Header = () => {
  const [user, setUser] = useState(null);

  const router = useRouter();
  const { ref, visible, setVisible } = useOutsideAlerter();

  // 修复:添加空依赖数组
  useEffect(() => {
    const user = localStorage.getItem("user");
    if (user) {
      setUser(JSON.parse(user));
    }
  }, []);

  const logUserOut = () => {
    signOut(auth)
      .then(() => {
        localStorage.removeItem("user");
        router.push("/");
      })
      .catch((error) => {
        console.error(error);
        alert("There was a problem signing out.");
      });
  };

  return (
    <div className="flex items-center justify-between p-4 border-b border-gray-300">
      <h1 className="font-bold text-lg">Matrice</h1>
      <div ref={ref}>
        <button
          onClick={() => setVisible(!visible)}
          className="hover:bg-gray-100 rounded-md p-2 flex items-center justify-center text-gray-400"
        >
          {user?.displayName || "Login"} <FaChevronDown className="ml-2" />
        </button>
        {visible && (
          <div
            className={
              "absolute top-14 right-4 rounded-md border border-gray-300 bg-white overflow-hidden"
            }
          >
            {user && (
              <button
                onClick={logUserOut}
                className={
                  "text-left text-sm hover:bg-blue-700 hover:text-white w-full px-4 py-2"
                }
              >
                Log Out
              </button>
            )}
            <button
              onClick={() =>
                router.replace(
                  "mailto:email"
                )
              }
              className={
                "text-left text-sm hover:bg-blue-700 hover:text-white w-full px-4 py-2"
              }
            >
              Request Feature
            </button>
          </div>
        )}
      </div>
    </div>
  );
};

export default Header;

内容的提问来源于stack exchange,提问作者RyanHaraki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 13:45:31