自定义Axios Hook异常:loading状态不符合预期的原因排查
自定义Axios Hook的loading状态异常问题解析
问题场景回顾
你编写第一个useAxios Hook后,在App组件中打印loading状态得到false false false,和预期的false true true false不符;修改Hook后输出变为false true false,符合预期。下面来拆解问题根源:
第一个Hook的核心错误
并不是不能在useEffect中多次调用setState,而是你的写法没有处理好React状态更新的异步性和axios请求的异步流程,具体问题如下:
React状态更新的批量合并特性
看第一个Hook的useEffect代码:useEffect(() => { setState(currentState => ({ ...currentState, loading: true })); axios.get(url).then(response => { setState(currentState => ({ ...currentState, data: response.data.body })); }); setState(currentState => ({ ...currentState, loading: false })); }, [url]);在同步代码块里,你连续调用了两次
setState修改loading:先设为true,紧接着又设为false。React会对同步执行的状态更新进行批量合并,最终只会保留最后一次的更新结果——也就是loading: false,直接跳过了loading: true的中间状态。异步请求的执行顺序问题
axios的get请求是异步操作,它的then回调会被放入事件队列,等到当前同步代码全部执行完毕后才会触发。所以代码的实际执行顺序是:- 执行
setState(loading: true)(React暂存更新,未立即重渲染) - 发起axios异步请求(进入事件队列等待)
- 执行
setState(loading: false)(React合并两次loading更新,最终状态为false) - 当前同步代码执行完成,React触发第一次重渲染,此时
loading为false(第一次打印false) - 后续axios请求完成,执行
then回调更新data,此时loading依然是false,触发第二次重渲染(打印false) - 加上组件初始渲染的
false,最终得到三次false的输出
- 执行
修改后Hook的正确性解释
修改后的Hook把loading: false的设置放在了axios请求的then回调中:
useEffect(() => { setState(currentState => ({ ...currentState, loading: true })); axios.get(url).then(response => { setState(currentState => ({ loading: false, data: response.data.body })); }); }, [url]);
执行顺序变成:
- 组件初始渲染:
loading为false(第一次打印false) useEffect执行,调用setState将loading设为true,React触发重渲染(打印true)- axios请求完成,
then回调执行,将loading设为false并更新data,触发第三次重渲染(打印false)
最终输出false true false,完全符合异步请求的状态变化逻辑。
总结
第一个Hook的错误点:
- 错误地在同步代码中连续修改
loading状态,导致React合并更新,丢失了loading: true的中间状态 - 没有将
loading: false的更新逻辑绑定到异步请求的完成回调中,而是同步执行,导致请求还未完成就把loading切回了false
内容的提问来源于stack exchange,提问作者jwkoo
相关产品推荐
相关产品推荐

