为何使用useEffect在页面渲染时调用的函数会执行两次?
解决React中handleInitiateSearch函数重复执行两次的问题
问题分析
你遇到的情况大概率是React 18严格模式在开发环境下的刻意行为——严格模式会双重执行useEffect、组件初始化逻辑等,目的是提前检测潜在的副作用问题,但生产环境不会触发这个机制。如果禁用严格模式后仍无效,可能是组件被多次挂载,或者存在其他触发重渲染的逻辑。
解决方案
1. 正确关闭React严格模式
检查你的根组件文件(比如layout.tsx或_app.tsx),移除<React.StrictMode>标签。开发环境下关闭后,useEffect就只会执行一次:
// layout.tsx(Next.js 13+)示例 import type { Metadata } from 'next' import { Inter } from 'next/font/google' const inter = Inter({ subsets: ['latin'] }) export const metadata: Metadata = { title: 'Create Next App', description: 'Generated by create next app', } // 移除<React.StrictMode>包裹 export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( <html lang="en"> <body className={inter.className}>{children}</body> </html> ) }
2. 用状态标记控制执行次数
如果需要保留严格模式,或者要在特定重渲染场景下重新执行函数,可以添加状态标记来限制执行,同时支持手动触发重新运行:
"use client"; import { useEffect, useState } from "react"; export default function Home() { const [loading, setLoading] = useState(false); const [roads, setRoads] = useState([]); const [error, setError] = useState(false); const [errorMessage, setErrorMessage] = useState(""); // 标记是否已执行过初始搜索 const [hasRunInitialSearch, setHasRunInitialSearch] = useState(false); // 触发重新执行的开关,每次递增数值来触发useEffect const [refreshFlag, setRefreshFlag] = useState(0); useEffect(() => { // 首次加载或手动触发刷新时执行 if (!hasRunInitialSearch || refreshFlag > 0) { handleInitiateSearch(); setHasRunInitialSearch(true); } }, [refreshFlag]); async function handleInitiateSearch() { setLoading(true); try { const roadsResponse = await fetch(`/api?road=all`); const roadsData = await roadsResponse.json(); console.log("Fetch response:", roadsData); setRoads(roadsData.roads); if (roadsResponse.status === 201) { setError(true); setErrorMessage(roadsResponse.statusText); } } finally { setLoading(false); } } // 手动触发重新搜索的方法 const triggerRefresh = () => { setHasRunInitialSearch(false); setRefreshFlag(prev => prev + 1); }; return ( <div> {loading && <p>加载中...</p>} <button onClick={triggerRefresh}>重新获取数据</button> {/* 组件其他内容 */} </div> ); }
3. 让请求幂等/取消重复请求(解决GitLab流水线问题)
如果重复请求破坏了GitLab流水线,优先让后端API支持幂等性(比如用唯一请求ID,确保重复请求不会触发重复流水线任务)。前端也可以用AbortController取消未完成的重复请求:
"use client"; import { useEffect, useState } from "react"; export default function Home() { // ... 其他状态 const [currentAbortController, setCurrentAbortController] = useState(null); useEffect(() => { handleInitiateSearch(); }, []); async function handleInitiateSearch() { setLoading(true); // 创建新的控制器,取消之前未完成的请求 const controller = new AbortController(); if (currentAbortController) { currentAbortController.abort(); } setCurrentAbortController(controller); try { const roadsResponse = await fetch(`/api?road=all`, { signal: controller.signal }); const roadsData = await roadsResponse.json(); console.log("Fetch response:", roadsData); setRoads(roadsData.roads); if (roadsResponse.status === 201) { setError(true); setErrorMessage(roadsResponse.statusText); } } catch (error) { // 忽略请求被取消的错误 if (error.name !== 'AbortError') { setError(true); setErrorMessage(error.message); } } finally { setLoading(false); setCurrentAbortController(null); } } // ... 组件渲染内容 }
内容的提问来源于stack exchange,提问作者IdkAskAiOrSum
相关产品推荐
相关产品推荐

