React组件挂载调用API后设置定时调用避免重复请求的问题
问题原因
- useEffect依赖项包含
appState.appointmentList、appState.loading,API请求完成后这两个状态都会更新,触发useEffect反复重执行,每次执行都会新增定时器、发起新请求,直接导致重复调用 - 原请求触发逻辑写反:
appState.loading为true时才发起请求,不符合正常的请求发起逻辑 - 没有做请求互斥处理,可能出现上一个请求未完成、下一个定时请求又发起的并发问题
- 未适配React18严格模式下useEffect默认执行两次的逻辑,会额外多触发一次初始请求
修复后代码
import fetchAppointments from 'src/redux/asyncThunkApiMethods'; import { useAppDispatch, useAppSelector } from 'src/redux/hooks'; import React, { useEffect, useState, useRef } from 'react'; import { useLocation } from 'react-router-dom'; import { Table, TableHead, TableRow, TableCell, TableBody, Box, Typography } from '@material-ui/core'; import classes from '../../styles/AppointmentList.module.css'; const Appointments = (): JSX.Element => { const appState = useAppSelector((state) => state.appointments); const dispatch = useAppDispatch(); const location = useLocation(); const [isValid, setIsValid] = useState(false); // 请求互斥锁,防止重复发起请求 const isRequestingRef = useRef(false); const bac = location.pathname.split('/')[5]; // 单独处理isValid状态更新,和请求逻辑分离 useEffect(() => { if (!appState.loading && !appState.errorMessage && appState.appointmentList?.visits?.length) { setIsValid(true); } }, [appState.loading, appState.errorMessage, appState.appointmentList]); // 处理初始请求和定时请求,只依赖bac和dispatch useEffect(() => { // 封装请求方法,加互斥锁 const fetchData = async () => { if (isRequestingRef.current) return; isRequestingRef.current = true; try { await dispatch(fetchAppointments({ bac })).unwrap(); } catch (err) { console.error('请求预约列表失败', err); } finally { isRequestingRef.current = false; } }; // 组件挂载后立即发起第一次请求 fetchData(); // 设置20秒定时请求 const interval = setInterval(fetchData, 20000); // 组件卸载/bac变化时清理定时器 return () => { clearInterval(interval); isRequestingRef.current = false; }; }, [bac, dispatch]); // 剩下的渲染逻辑保持不变 return ( <div className={classes.root}> {/* 原有表格等渲染内容 */} </div> ); }; export default Appointments;
核心修改点
- 拆分两个useEffect:一个单独处理
isValid状态更新,依赖redux相关状态;另一个单独处理请求逻辑,只依赖bac和dispatch,避免请求逻辑被redux状态变化反复触发 - 新增
useRef实现请求互斥锁,不管是初始请求还是定时请求,只要上一个请求没完成就不会发起新请求,彻底避免重复调用 - 适配React18严格模式的重复执行逻辑,互斥锁会挡住第二次无效的初始请求
- 用async thunk的
unwrap()方法处理请求异常,避免未捕获的报错中断代码执行 - 定时器逻辑和请求逻辑解耦,每次定时执行都会先判断请求状态,不会并发调用接口
内容的提问来源于stack exchange,提问作者jsanchez
相关产品推荐
相关产品推荐

