Next.js中如何让useEffect仅执行一次?解决重复调用问题
Next.js中解决useEffect触发两次的问题
问题原因
React 18的Strict Mode在开发环境下会刻意重复执行副作用(包括useEffect),用于检测潜在的代码问题,而Next.js默认启用该模式,这是你看到useEffect触发两次的核心原因。另外你的代码中useEffect依赖数组为空,但内部用到了token和router,存在闭包隐患,也需要修复。
以下是适配Next.js的几种解决方案:
方案一:服务端预获取数据(最优)
既然你已经使用getServerSideProps,完全可以在服务端直接调用userDetails获取数据,无需等到客户端渲染再发起请求。这种方式既避免了客户端重复触发问题,还能减少一次网络请求,提升首屏性能。
修改后的代码(中文注释):
import axios from "axios"; import React from "react"; import nookies from "nookies"; import { userDetails } from "../../libs/chats"; import { useRouter } from "next/router"; import Index from "./index"; // 接收服务端传递的token和用户详情数据 const Friends = ({ token, userData }: any) => { const router = useRouter(); const handleLogout = async () => { const data = await axios.post("/api/auth/logout"); if (data.status === 200) { router.push("/?login"); } }; // 客户端仅处理用户名校验逻辑 React.useEffect(() => { if (!userData?.username) { prompt("请输入用户名: "); } }, [userData]); // 无token时直接跳转登录(双重保险) if (!token) { router.push("/?login"); return null; } return ( <Index> <button onClick={handleLogout}>退出登录</button> </Index> ); }; export default Friends; export const getServerSideProps = async (ctx: any) => { const cookies = nookies.get(ctx); const token = cookies.token; // 无token直接返回,客户端跳转登录 if (!token) { return { props: { token: null, userData: null }, }; } try { // 服务端调用接口获取用户数据 const { data } = await userDetails(token); return { props: { token, userData: data }, }; } catch (err) { // 请求失败时清除token并跳转登录 nookies.destroy(ctx, "token"); return { redirect: { destination: "/?login", permanent: false, }, }; } };
方案二:修复依赖+标记首次执行(适配客户端请求场景)
如果必须在客户端发起请求,先修复useEffect的依赖数组问题,再用ref标记首次执行,避免开发环境重复触发:
修改后的useEffect部分代码:
const Friends = ({ token }: any) => { const router = useRouter(); // 标记是否为首次执行副作用 const isFirstRun = React.useRef(true); const handleLogout = async () => { const data = await axios.post("/api/auth/logout"); if (data.status === 200) { router.push("/?login"); } }; React.useEffect(() => { // 开发环境下第二次触发时直接跳过 if (!isFirstRun.current) return; isFirstRun.current = false; const getFriends = async () => { const { data } = await userDetails(token); if (!data.username) { prompt("请输入用户名: "); } }; if (!token) { router.push("/?login"); return; } getFriends(); }, [token, router]); // 添加用到的依赖变量,避免闭包问题 // 其余代码不变 };
方案三:关闭Strict Mode(不推荐)
如果只是开发环境不想看到重复触发,可以在next.config.js中关闭Strict Mode,但不建议这么做——该模式能帮你提前发现潜在的副作用问题:
/** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: false, }; module.exports = nextConfig;
内容的提问来源于stack exchange,提问作者Kartik Fulara
相关产品推荐
相关产品推荐

