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

Next.js中useAsync钩子函数重复执行两次问题求助

解决useAsync钩子执行两次的问题

核心原因分析

  1. React Strict Mode 行为:Next.js在开发模式下默认启用Strict Mode,它会刻意重复执行useEffect的回调函数,目的是检测代码中潜在的副作用问题,生产环境不会出现此情况。
  2. 依赖项触发重复执行:如果useUser返回的user.id存在从undefined到有效值的变化,会导致useEffect的依赖项更新,进而触发第二次执行。

解决方案

方案1:利用Ref标记首次执行(解决开发环境重复执行)

在useAsync中添加一个Ref来跟踪是否已经完成首次执行,避免Strict Mode导致的重复调用:

import { useCallback, useEffect, useState, useRef } from "react";
import useUser from "./useUser";

export const useAsync = (func: Function, dependencies = [], requireAuth = true) => {
    const { execute, ...state } = useAsyncInternal(func, dependencies, true);
    const user = useUser();
    const hasExecuted = useRef(false);

    useEffect(() => {
        // 已执行过则跳过
        if (hasExecuted.current) return;
        
        if (requireAuth) {
            if (user.id) {
                execute();
                hasExecuted.current = true;
            }
        } else {
            execute();
            hasExecuted.current = true;
        }

        // 当user.id变为无值时重置标记,以便后续重新触发
        return () => {
            if (!user.id) hasExecuted.current = false;
        };
    }, [execute, user.id]);

    return state;
}

// useAsyncInternal 代码保持不变
const useAsyncInternal = (func: Function, dependencies = [], initialLoading = false) => {
    const [loading, setLoading] = useState(initialLoading);
    const [error, setError] = useState<any>();
    const [data, setData] = useState<any>();

    const execute = useCallback((...params: any[]) => {
        setLoading(true);

        return func(...params).then((data: any) => {
            setData(data);
            setError(undefined);
            return data;
        }).catch((error: any) => {
            setData(undefined);
            setError(error);
            return Promise.reject(error);
        }).finally(() => {
            setLoading(false);
        });
    }, dependencies);

    return { loading, error, data, execute }
}

方案2:调整Strict Mode配置(仅开发环境)

如果不需要Strict Mode的检测功能,可以在Next.js项目中关闭它:

  • App Router:修改next.config.js,添加reactStrictMode: false
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: false,
};

module.exports = nextConfig;
  • Pages Router:修改pages/_app.tsx,移除<React.StrictMode>包裹

额外优化点

检查useAsyncInternal的initialLoading参数是否必要:当前设置为true会让loading初始状态为true,但并未触发函数执行,如果不需要初始loading状态,可以将调用时的第三个参数改为false,避免不必要的loading状态显示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 02:15:33