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

NextJS 13.4+ App Router中useEffect执行3次的原因咨询

NextJS 13.4+ App Router中useEffect空依赖仍执行3次的原因及解决办法

原因分析

  • React 18严格模式的双重执行:开发环境下,React 18的Strict Mode会强制重复执行useEffect、组件函数等两次,目的是检测未正确清理的副作用问题,这是开发环境专属行为,生产环境会自动恢复单次执行。
  • App Router的渲染流程:即使页面标记了'use client',Next.js仍会先在服务器端完成初始渲染生成静态HTML,随后在客户端进行hydration(将静态HTML转为可交互组件),这次hydration会触发一次useEffect,加上Strict Mode的两次,就会出现总共3次执行。
  • 代码依赖缺失的潜在影响:getData函数定义在组件内部,每次组件渲染都会重新创建该函数,虽然你没将其加入useEffect依赖数组,但Strict Mode的检测机制可能间接导致额外执行(同时会引发ESLint警告)。

解决方法

1. 临时禁用Strict Mode(不推荐长期使用)

如果只是临时验证,可以在next.config.js中关闭Strict Mode,但不建议长期这么做,因为它能帮你提前发现很多潜在问题:

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: false,
}

module.exports = nextConfig

2. 用useCallback缓存函数并补充依赖

使用useCallback包裹getData,确保它不会随组件渲染重复创建,再将其加入useEffect依赖数组,既符合React规则,也能避免不必要的执行:

'use client';
import React, { useEffect, useState, useCallback } from 'react'

export default function Page() {
    const [myData, setMyData] = useState([]);

    const getData = useCallback(async () => {
        const response = await fetch('/api/data', {
            method: 'GET',
        });

        if (response.ok) {
            console.log('Response.Ok');
            const { data } = await response.json();
            setMyData(data);
        } else {
            console.log('Error');
        }
    }, []);

    useEffect(() => {
      getData();
    }, [getData])

    return (
        <div>
            {myData.map((item, index) => (
                <div key={index}>{item.name.toUpperCase()}</div>
            ))}
        </div>
    )
}

3. 改用服务器组件预获取数据(推荐)

遵循Next.js App Router的最佳实践,直接在服务器组件中获取数据,数据会在服务器端预获取,无需客户端useEffect,从根源避免多次执行问题:

// /app/page.js(无需'use client'标记)
export default async function Page() {
    const response = await fetch('http://localhost:3000/api/data', {
        method: 'GET',
        cache: 'no-store' // 根据需求设置缓存策略,如'static'、'force-cache'等
    });

    if (!response.ok) {
        throw new Error('Failed to fetch data');
    }

    const { data } = await response.json();

    return (
        <div>
            {data.map((item, index) => (
                <div key={index}>{item.name.toUpperCase()}</div>
            ))}
        </div>
    )
}

内容的提问来源于stack exchange,提问作者Octo Palm Tree

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 01:53:12