React严格模式下接收正确数据仍返回400 Bad Request问题排查
问题原因与解决方案
核心原因
React Strict Mode在开发环境会刻意重复调用异步操作、副作用函数(如useEffect、事件触发的异步逻辑),目的是检测代码中的不稳定副作用。你的场景下,updateLeaderboard被触发了两次:
- 第一次请求成功写入数据,后端返回201;
- 第二次请求重复执行插入操作,触发后端的错误逻辑(比如数据库Schema对
questionResultList中的playerId有唯一约束,或重复插入导致的验证失败),因此返回400。
验证步骤
- 前端:在
updateQuestionLeaderboardaction中添加请求次数打印,确认是否发送了两次请求:console.log('Sending request, count:', window.requestCount ? window.requestCount++ : window.requestCount = 1) - 后端:在Controller的
catch块中打印完整错误信息,明确报错原因:catch (error) { console.error('Full error details:', error); // 打印完整错误 res.status(400).json({ message: error.message, stack: error.stack }); }
解决方案
后端(根本解决,保证接口幂等性)
修改Controller逻辑,确保重复执行请求不会导致错误:
- 先检查当前题目对应的排行榜中是否已存在该玩家的记录,存在则更新分数,不存在再插入:
export const updateQuestionLeaderboard = async (req, res) => { const { leaderboardId } = req.params; const { questionIndex, playerId, playerPoints } = req.body; let leaderboard; try { leaderboard = await Leaderboard.findById(leaderboardId); const questionLeaderboard = leaderboard.questionLeaderboard[questionIndex]; // 检查是否已有该玩家的记录 const existingPlayer = questionLeaderboard.questionResultList.find(item => item.playerId === playerId); if (existingPlayer) { // 若存在则更新分数(根据需求调整逻辑) existingPlayer.playerPoints = playerPoints; } else { // 不存在则新增 questionLeaderboard.questionResultList.push({ playerId, playerPoints }); } const newLeaderboard = await leaderboard.save(); console.log({ newLeaderboard }); res.status(201).json(newLeaderboard); } catch (error) { console.error('Update error:', error); res.status(400).json({ message: error.message }); } };
- 检查
LeaderboardSchema:如果questionResultList中的playerId设置了unique: true,可以移除该约束(如果允许同一玩家多次提交),或者保留约束并使用findOneAndUpdate配合upsert逻辑。
前端(兼容Strict Mode开发环境)
确保触发请求的逻辑不会重复执行:
- 如果
updateLeaderboard是由useEffect触发,添加依赖数组并确保依赖稳定; - 给请求添加防抖逻辑,避免短时间内重复触发:
// 自己实现防抖函数 const debounce = (func, delay) => { let timeoutId; return (...args) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(this, args), delay); }; }; const debouncedUpdateLeaderboard = useCallback(debounce((data, id, score) => { // 原updateLeaderboard逻辑 }, 300), [dispatch, token]);
补充说明
React Strict Mode的重复执行仅存在于开发环境,生产环境不会有此行为,但保证接口的幂等性(重复调用无副作用)是后端接口的最佳实践,能避免各种异常场景。
内容的提问来源于stack exchange,提问作者Strike
相关产品推荐
相关产品推荐

