ReactJs问答应用提交分数至API时userScore始终为0解决方案
React问答应用提交userScore始终为0问题修复
核心问题定位
分数提交始终为0是React状态异步更新+函数返回值缺失+执行时序错误三个问题叠加导致的:
- React的
useState状态更新不是同步生效的,调用setScoreAfterQuiz后,当前函数执行栈内读到的scoreAfterQuiz还是更新前的初始值0,新值要等组件下一次重渲染才会生效。在调用handleScore后立刻执行addScore时,addScore读到的scoreAfterQuiz必然是0。 - 现有
handleScore只做了内部计数和状态更新,没有返回计算结果,const scoreResult = handleScore()拿到的实际是undefined,后续setScore、addScore传这个值本身就不符合逻辑。 - 提交函数里重复调用了两次
handleScore,做无意义的重复计算,还可能触发重复的状态更新。 - 附带问题:百分比计算逻辑写的过于绕,
((s*10)/questions.length)*10完全可以简化为s/questions.length * 100,可读性更高,不容易出计算偏差。
分步修复方案
1. 改造计分函数,直接返回计算结果
修改handleScore,保证计算逻辑正确的同时,把最终得分作为返回值传出,不需要等状态更新就能拿到结果:
const handleScore = () => { let correctCount = 0 for(let i = 0; i < questions.length; i++){ // 替换==为===做严格相等判断,避免隐式类型转换导致的判题错误 if(questions[i].answer === myAnswers[i]){ correctCount++ } } // 简化百分比计算逻辑,直接得到百分制得分 const finalScore = Math.round(correctCount / questions.length * 100) // 更新页面展示用的状态 setScoreAfterQuiz(finalScore) // 关键:返回计算好的分数,供提交逻辑直接使用 return finalScore }
2. 调整提交函数逻辑,去掉重复调用
修改handleSubmitScoreAndName,只执行一次计分逻辑,直接拿到分数结果用于后续操作,不要依赖状态更新:
const handleSubmitScoreAndName = (event) => { event.preventDefault(); handleChange(event); setDisplayName(event.target.value); // 仅执行一次计分,直接拿到最终得分 const scoreResult = handleScore(); setScore(scoreResult) // 把分数作为参数传入提交方法,不依赖组件状态 addScore(scoreResult) handleSubmit(event); }
3. 改造接口提交函数,接收外部传入的分数
修改addScore接收分数参数,直接用传入的正确分数组装请求参数,彻底避免读取旧状态的问题:
// 增加入参接收计算好的分数 const addScore = (userScore) => { const newScore = { displayName:displayName, userScore: userScore, // 直接使用传入的正确分数 quizId, appUserId:auth.user.appUserId }; console.log(newScore) const init = { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${auth.user.token}` }, body: JSON.stringify(newScore) }; fetch('http://localhost:8080/api/score', init) .then(response => { if (response.status === 201 || response.status === 400) { return response.json(); } else { return Promise.reject(`Unexpected status code: ${response.status}`); } }) .then(data => { if (data.scoreId) { history.push(`/finalscore`); } else { setErrors(data); } }) .catch(console.log); };
补充说明
如果后续有逻辑必须依赖状态更新后的值执行,可以用useEffect监听对应状态的变化再触发逻辑,但当前提交场景下,直接使用函数返回值是时序最稳定、代码最简洁的方案,完全可以避开React状态异步更新带来的坑。
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

