Next.js关闭Strict Mode后API仍多次触发的问题及解决方法
解决Next.js中React Query API重复调用四次的问题
问题分析
当selectedAmc值变更时,API被触发四次,即使关闭Next.js严格模式也无法解决。当前代码通过useEffect手动调用React Query的refetch方法触发请求,核心问题可能出在:
refetch函数引用不稳定,导致useEffect因依赖变化重复执行- 组件不必要的重复渲染
- 未利用React Query内置的请求自动管理机制,手动触发逻辑存在冗余
解决方案
方案1:改用React Query自动触发请求(推荐)
利用React Query的查询键和enabled配置,让框架自动管理请求触发,避免手动副作用的问题:
import { Select } from "antd"; import React from "react"; import { useQuery } from "react-query"; import { postSchemeName } from "../../../pages/mis-reports/mis-api"; const { Option } = Select; type Props = { className?: string; selectedAmc?: string; }; const SchemeName = ({ className, selectedAmc }: Props) => { // 自动触发查询,无需手动refetch和useEffect const { data: schemeData } = useQuery<any, any>( // 查询键:selectedAmc变化时自动视为新查询 ["post-scheme", selectedAmc], // 明确传递API所需参数 () => postSchemeName({ fund_house_name: selectedAmc }), { // 仅当selectedAmc存在时执行查询 enabled: !!selectedAmc, // 关闭非必要的自动重请求,避免额外调用 refetchOnWindowFocus: false, refetchOnReconnect: false, // 设置缓存时间,相同参数自动复用缓存 cacheTime: 5 * 60 * 1000, // 5分钟 } ); const onChange = (value: string) => {}; const onSearch = (value: string) => {}; console.log("selected amc--->", selectedAmc); return ( <> <div className="mb-4 xxs:w-[100%] xs:w-[45%] sm:w-[30%]"> <p className="mb-1">Scheme Name</p> <Select className={`h-10 ${className} w-full`} showSearch size="large" placeholder="Scheme Name..." optionFilterProp="children" onChange={onChange} onSearch={onSearch} filterOption={(input, option) => (option!.children as unknown as string) .toLowerCase() .includes(input.toLowerCase()) } > {/* 用接口返回数据渲染选项 */} {schemeData?.map(scheme => ( <Option key={scheme.value} value={scheme.value}> {scheme.label} </Option> )) || <Option value="hellp">hello</Option>} </Select> </div> </> ); }; // 用React.memo避免不必要的重渲染 export default React.memo(SchemeName);
方案2:手动触发时限制重复调用(兼容原有逻辑)
如果必须保留手动触发逻辑,可通过useRef标记请求状态,避免重复执行:
import { Select } from "antd"; import React, { useEffect, useRef } from "react"; import { useQuery } from "react-query"; import { postSchemeName } from "../../../pages/mis-reports/mis-api"; const { Option } = Select; type Props = { className?: string; selectedAmc?: string; }; const SchemeName = ({ className, selectedAmc }: Props) => { const { data: schemeData, refetch: fetchSchemeName } = useQuery<any, any>( ["post-scheme", { fund_house_name: selectedAmc }], postSchemeName, { enabled: false, } ); // 标记当前selectedAmc是否已触发过请求 const isFetched = useRef(false); useEffect(() => { if (!selectedAmc) return; // 同一selectedAmc值只触发一次请求 if (isFetched.current) return; fetchSchemeName() .then(() => { isFetched.current = true; }) .catch(() => { // 请求失败时重置标记,允许重试 isFetched.current = false; }); // selectedAmc变化时重置标记 return () => { isFetched.current = false; }; }, [selectedAmc, fetchSchemeName]); const onChange = (value: string) => {}; const onSearch = (value: string) => {}; console.log("selected amc--->", selectedAmc); return ( <> <div className="mb-4 xxs:w-[100%] xs:w-[45%] sm:w-[30%]"> <p className="mb-1">Scheme Name</p> <Select className={`h-10 ${className} w-full`} showSearch size="large" placeholder="Scheme Name..." optionFilterProp="children" onChange={onChange} onSearch={onSearch} filterOption={(input, option) => (option!.children as unknown as string) .toLowerCase() .includes(input.toLowerCase()) } > <Option value="hellp">hello</Option> </Select> </div> </> ); }; export default React.memo(SchemeName);
额外排查点
- 检查父组件中
selectedAmc是否被多次更新,导致组件重复触发请求 - 确认
postSchemeName接口内部是否存在重复调用逻辑
内容的提问来源于stack exchange,提问作者Safal Adhikari
相关产品推荐
相关产品推荐

