React中useEffect重复触发API调用问题求助
解决API重复调用问题的方案
问题根源
你当前的useEffect没有设置依赖数组,这意味着组件每一次重渲染(任何状态、上下文变化都会触发重渲染)都会执行这个effect里的逻辑,自然会导致API被多次调用。另外,即使加上依赖,快速切换分支/日期也可能触发连续请求,所以还要做防抖处理。
具体修复步骤
1. 给useEffect添加精准的依赖数组
把effect的依赖数组设置为[cities, selectedBranchIndex, selectedDate, branch],这样只有当这些关联值发生变化时,才会执行温度获取逻辑,避免无意义的重复执行。
2. 增加防抖逻辑
用useRef存储防抖定时器,每次触发请求前清除之前的定时器,延迟一段时间(比如500ms)再发起API调用,避免用户快速操作导致的连续请求。
3. 修复cities的校验逻辑
你代码里cities !== "undefined"是错误的,因为cities是上下文提供的对象,不是字符串,应该改成cities?.cities来判断是否有有效数据。
4. 可选:添加请求取消机制
用AbortController在组件重渲染或依赖变化时取消未完成的请求,避免旧请求返回覆盖最新结果。
修改后的完整代码
import Calendar from "../components/Calendar"; import Languages from "../components/Languages/Languages.json"; import React, { useContext, useEffect, useRef, useState } from "react"; import { ContextCities } from "../Contexts/ContextCities"; import { ContextLanguages } from "../Contexts/ContextLanguages"; import { loadBranches } from "../utils/branchesHandelApi"; import { loadWeather } from "../utils/weatherHandelApi"; export default function Transactions() { const { cities } = useContext(ContextCities); const { selectedLanguage } = useContext(ContextLanguages); const [branch, setBranch] = useState([]); const [selectedBranchIndex, setSelectedBranchIndex] = useState(0); const [selectedDate, setSelectedDate] = useState(); const [selectedDateData, setSelectedDateData] = useState(); const [weather, setWeather] = useState(0); // 防抖定时器Ref const debounceTimer = useRef(null); // 请求取消控制器Ref const abortControllerRef = useRef(null); const handleSubmitTransaction = async (e) => { e.preventDefault(); // saveTransactions(transaction); }; useEffect(() => { loadBranches(setBranch); }, []); useEffect(() => { // 初始挂载或参数无效时重置温度 if (selectedBranchIndex === 0 || !selectedDate || !cities?.cities) { setWeather(0); return; } // 清除之前的防抖定时器 if (debounceTimer.current) { clearTimeout(debounceTimer.current); } // 取消之前未完成的请求 if (abortControllerRef.current) { abortControllerRef.current.abort(); } debounceTimer.current = setTimeout(async () => { try { abortControllerRef.current = new AbortController(); const targetCity = branch[selectedBranchIndex - 1]?.city; if (!targetCity) return; const station = cities.cities.find(itm => itm.Name === targetCity)?.meteorological_station; if (!station?.id) return; console.log("station_id", station.id); // 假设loadWeather支持传入signal参数做请求取消 await loadWeather(station.id, selectedDate, setWeather, abortControllerRef.current.signal); } catch (err) { // 忽略请求取消的错误 if (err.name !== "AbortError") { console.error("获取天气失败:", err); setWeather(0); } } }, 500); // 500ms防抖延迟,可根据需求调整 // 清理函数:组件卸载或依赖变化时清除定时器和取消请求 return () => { clearTimeout(debounceTimer.current); if (abortControllerRef.current) { abortControllerRef.current.abort(); } }; }, [cities, selectedBranchIndex, selectedDate, branch]); // 核心:添加依赖数组 return ( <> <h1> <span style={{ fontSize: "2rem", fontFamily: "Noto Color Emoji" }}> {"💳"} </span>{" "} {Languages.transactions[selectedLanguage]} </h1> <form autoComplete="off" className="App" id="form" onSubmit={(e) => handleSubmitTransaction(e)} > <fieldset> <legend> <h2>{Languages.manual_update[selectedLanguage]}</h2> </legend> <h2>{Languages.branches[selectedLanguage]}</h2> <h3>{Languages.name[selectedLanguage]}:</h3>{" "} <select defaultValue="" name="branch" required onChange={(e) => setSelectedBranchIndex(e.target.selectedIndex)} > <option hidden value=""> {Languages.name[selectedLanguage]} </option> {branch.map((itm, index) => ( <option key={index}>{itm.name}</option> ))} </select>   <h3>{Languages.city[selectedLanguage]}:</h3>{" "} <input disabled="disabled" readOnly value={ selectedBranchIndex === 0 ? "" : branch[selectedBranchIndex - 1]?.city || "" } /> <br /> <br /> <h2>{Languages.date[selectedLanguage]}</h2> <Calendar setSelectedDate={setSelectedDate} setSelectedDateData={setSelectedDateData} /> <br /> <br /> <h2>{Languages.weather[selectedLanguage]}</h2> <h3>{Languages.meteorological_station_id[selectedLanguage]}:</h3>{" "} <input readOnly disabled="disabled" value={ selectedBranchIndex === 0 || !cities?.cities ? "" : cities.cities.find( (itm) => itm.Name === branch[selectedBranchIndex - 1]?.city )?.meteorological_station.id || "" } />   <h3> {Languages.meteorological_station_location[selectedLanguage]}: </h3>{" "} <input readOnly disabled="disabled" value={ selectedBranchIndex === 0 || !cities?.cities ? "" : cities.cities.find( (itm) => itm.Name === branch[selectedBranchIndex - 1]?.city )?.meteorological_station.name || "" } />   <h3>{Languages.temperature[selectedLanguage]}:</h3>{" "} <input readOnly disabled="disabled" value={weather} /> {" ℃"} </fieldset> </form> </> ); }
额外说明
- 防抖延迟时间可以根据实际需求调整,比如300ms-1000ms都可以。
- 如果你的
loadWeather函数不支持AbortSignal,可以去掉取消请求的部分,只保留防抖逻辑也能解决大部分重复调用问题。 - 代码中添加了可选链
?.,避免因为branch或cities数据未加载完成导致的报错。
内容的提问来源于stack exchange,提问作者Y.S.
相关产品推荐
相关产品推荐

