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
相关产品推荐
相关产品推荐

