React await请求后useState赋值消息第二次提交才显示如何解决
问题根因
出现提示永远慢一拍、第二次提交才展示第一次报错的核心原因是React State闭包陷阱,具体问题点有两个:
useAuth中的validateLogin函数逻辑错误:你在catch块中调用setMessage(messageError)更新内部state时,React的state更新是异步批处理的,不会同步修改当前函数作用域内捕获的message变量值。函数最终return的{ message }是本轮执行时闭包留存的旧值(第一次执行时是空字符串,第二次执行时是第一次请求存的报错),永远比真实请求结果慢一拍。- 状态冗余重复存储:
useAuth、useLoginForm两层都存了message状态,多一层状态拷贝就多一层不同步的风险,完全没必要。
修复步骤
- 移除
useAuth中冗余的message状态,直接用局部变量存储当次请求的报错信息,从根源规避闭包问题,修改后代码如下:
export function useAuth() { const [isLoading, setIsLoading] = useState(false); async function validateLogin(payload) { setIsLoading(true); // 用普通局部变量存本次请求结果,不存在闭包引用问题 let responseMessage = ""; try { await axios.post("user/login", JSON.stringify(payload)); } catch (handledError) { responseMessage = handledError.response?.data.error || "server problems..." } finally { setIsLoading(false); } // 直接返回当次请求的真实结果 return { message: responseMessage }; } return { validateLogin, isLoading } }
- 其余代码不需要改动:修复后第一次提交时,
await validateLogin(payload)拿到的就是当次请求的真实报错,setMessage更新后触发组件重渲染,useEffect监听到message更新、且isLoading为false时,会正常触发Toast展示,不会再出现延迟一拍的问题。
补充说明:如果确实需要在
useAuth中留存message状态供其他组件使用,不要靠return闭包值拿最新状态,应该通过useRef存最新值、或者把message状态也从useAuth中return出去,直接依赖useAuth返回的message状态变化做逻辑,不要在异步函数里拿set之后的state值当返回值。
内容的提问来源于stack exchange,提问作者Thalys Farias da Silva
相关产品推荐
相关产品推荐

