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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 14:25:24