React+Express应用单次Axios PATCH请求重复触发问题排查
React+Node.js应用中Axios PATCH请求重复触发的问题排查与解决思路
问题描述
在React(TypeScript)+Node.js/Express应用中,客户端通过Axios发起单次PATCH请求时,服务器会收到两个间隔约1秒的相同请求,导致日志重复、数据库产生重复条目。Postman发起相同请求无此问题,确认问题源于客户端。
客户端代码
SyncWorker组件(简化版)
该组件用于应用加载时同步特定项,已精简至基础形式仍出现重复请求:
import { useCallback, useEffect, useRef } from "react"; import axios from "axios"; function SyncWorker() { const processingIdsRef = useRef<Set<string>>(new Set()); const syncItems = useCallback(async (unsynced: any[]) => { for (const item of unsynced) { // 改用业务唯一标识ongoing_job_id作为锁键,原代码依赖的item.id不存在,导致锁逻辑失效 const uniqueId = item.ongoing_job_id; if (processingIdsRef.current.has(uniqueId)) continue; processingIdsRef.current.add(uniqueId); try { const response = await axios.patch( `${import.meta.env.VITE_PUBLIC_SERVER_BASE_URL}/ongoing-jobs/configure-device`, { ongoing_job_id: item.ongoing_job_id, type: item.type, image_url: item.image_url, position_identifier: item.position_identifier, serial_number: item.serial_number, } ); console.log(response.data); } catch (err) { console.error("Failed to update monitor: ", err); } finally { processingIdsRef.current.delete(uniqueId); } } }, []); useEffect(() => { const unsynced: any[] = [ { ongoing_job_id: "ongoing_job_id_424242", type: "fan", image_url: "www.image-url.com", position_identifier: "1.0.0", serial_number: "9", }, ]; syncItems(unsynced).catch(console.error); // 可添加日志排查执行次数:console.log("SyncWorker useEffect triggered"); }, []); return null; } export default SyncWorker;
App.tsx中组件使用
// App.tsx部分代码 function App() { return ( <> <ToastContainer /> <ContextWrapper> <SyncWorker /> <Router> <Routes> {/* 路由配置 */} </Routes> </Router> </ContextWrapper> </> ); }
服务端简化路由处理器
// Node.js/Express路由代码 router.patch("/ongoing-jobs/configure-device", async (req, res) => { try { const { position_identifier } = req.body; console.log("Configuring device for:", position_identifier); // 单次客户端请求触发两次日志 res.status(200).send("Configuring device for: " + position_identifier); } catch (err) { console.error("Failed to update job: ", err.message); res.status(500).send({ error: err.message }); } });
关键观察
- React Strict Mode已禁用
- 本地与部署环境均存在该问题
- Postman测试无重复请求,确认问题源于客户端
排查与解决思路
1. 修复客户端锁逻辑失效问题
原代码中processingIdsRef依赖item.id作为唯一标识,但测试数据中无id字段,导致item.id!为undefined,锁逻辑完全失效。改用ongoing_job_id(业务唯一标识)作为锁的键,确保同一请求不会被重复执行。
2. 排查Axios相关问题
- 检查Axios配置:确认是否全局注册了重复的请求/响应拦截器,或开启了自动重试逻辑(如第三方retry插件),这类配置可能导致请求被重复发送。
- 升级Axios版本:某些旧版本Axios存在PATCH请求重复的已知bug,尝试升级至最新稳定版(如
axios@1.x)。 - 查看浏览器Network面板:在开发者工具中检查是否有两个PATCH请求发出,查看请求的
Initiator字段,确认请求触发源(如React渲染、第三方脚本等)。
3. 排查React组件挂载/执行逻辑
- 检查组件是否重复挂载:在SyncWorker的
useEffect中添加日志(如console.log("SyncWorker mounted")),确认组件是否被多次挂载。排查ContextWrapper或父组件是否存在重复渲染逻辑。 - 验证useEffect执行次数:在
useEffect内添加日志,确认syncItems是否被调用了两次。即使useEffect依赖为空数组,若组件被重复挂载,也会多次执行。
4. 排除外部干扰
- 禁用浏览器扩展:部分广告拦截器、代理工具或调试插件可能修改或重复发送请求,禁用所有扩展后测试。
- 排除CORS预检干扰:PATCH请求会触发OPTIONS预检,但这与重复PATCH请求无关,可忽略。
内容的提问来源于stack exchange,提问作者Tsabary
相关产品推荐
相关产品推荐

