NextJS调用router.push跳转时触发Maximum update depth exceeded错误
问题现象
在NextJS的Dashboard组件中,尝试通过router.push("/dashboard/login")将未认证用户跳转至登录页面时,出现以下问题:
- 未处理运行时错误:
Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
- 控制台警告:
Warning: Cannot update a component (
Router) while rendering a different component (Dashboard). To locate the bad setState() call insideDashboard.
错误触发位置为代码中的router?.push("/dashboard/login");行。
问题原因
直接在组件渲染阶段调用router.push会触发路由状态更新,路由更新又会导致Dashboard组件重新渲染,重新渲染时再次执行router.push,形成无限循环。同时React禁止在组件渲染过程中更新其他组件(如Router),因此抛出警告。
解决方案
将跳转逻辑移至useEffect钩子中,useEffect会在组件渲染完成后执行,不会干扰渲染流程,也不会触发无限循环。
修改后的关键代码
const Dashboard = () => { const session = useSession(); const router = useRouter(); // ...其他代码 useEffect(() => { if (session.status === "unauthenticated") { router?.push("/dashboard/login"); } }, [session.status, router]); // ...其他代码 };
完整修改后的组件代码
"use client"; import React, { useEffect, useState } from "react"; import styles from "./page.module.css"; import useSWR from "swr"; import { useSession } from "next-auth/react"; import { useRouter } from "next/navigation"; import Image from "next/image"; const Dashboard = () => { const session = useSession(); const router = useRouter(); // 数据获取函数 const fetcher = (...args) => fetch(...args).then((res) => res.json()); const { data, mutate, error, isLoading } = useSWR( `/api/posts?username=${session?.data?.user.name}`, fetcher ); useEffect(() => { // 在渲染完成后处理跳转 if (session.status === "unauthenticated") { router?.push("/dashboard/login"); } }, [session.status, router]); if (session.status === "loading") { return <p>Loading...</p>; } const handleSubmit = async (e) => { e.preventDefault(); const title = e.target[0].value; const desc = e.target[1].value; const img = e.target[2].value; const content = e.target[3].value; try { await fetch("/api/posts", { method: "POST", body: JSON.stringify({ title, desc, img, content, username: session.data.user.name, }), }); mutate(); e.target.reset(); } catch (err) { console.log(err); } }; const handleDelete = async (id) => { try { await fetch(`/api/posts/${id}`, { method: "DELETE", }); mutate(); } catch (err) { console.log(err); } }; if (session.status === "authenticated") { return ( <div className={styles.container}> <div className={styles.posts}> {isLoading ? "loading" : data?.map((post) => ( <div className={styles.post} key={post._id}> <div className={styles.imgContainer}> <Image src={post.img} alt="" width={200} height={100} /> </div> <h2 className={styles.postTitle}>{post.title}</h2> <span className={styles.delete} onClick={() => handleDelete(post._id)} > X </span> </div> ))} </div> <form className={styles.new} onSubmit={handleSubmit}> <h1>Add New Post</h1> <input type="text" placeholder="Title" className={styles.input} /> <input type="text" placeholder="Desc" className={styles.input} /> <input type="text" placeholder="Image" className={styles.input} /> <textarea placeholder="Content" className={styles.textArea} cols="30" rows="10" ></textarea> <button className={styles.button}>Send</button> </form> </div> ); } // 避免组件无返回值的问题 return null; }; export default Dashboard;
额外说明
- 添加
useEffect的依赖数组[session.status, router],确保只有当会话状态变化或router实例更新时才执行跳转逻辑。 - 在组件末尾添加
return null;,避免当session.status为unauthenticated时组件没有返回值的问题。
内容的提问来源于stack exchange,提问作者Shehbaz

