在React中产生竞态请求的原因,主要来自于Promise的本质和React的生命周期
现象
在useEffect
中发送fetch
请求,当请求未结束时,我们切换了React的组件展示状态,页面会被切换,但是请求并没有结束,此时我们看到的是新的页面,但是但请求结束之后,旧的请求数据会覆盖到新的页面上。
例如一个有两个Tab的列表,两个列表UI不同,但是数据源都依赖一个 list state
, 切换Tab时页面会发生改变,接口会根据不同的Tab传递不同的参数,同时返回的数据也是不一样的,当我们处于TabA时,快速的切换到TabB,接口请求TabB对应的数据,再快速切换到TabA,此时TabB的接口还未结束请求,发送TabA的请求,TabA请求返回,页面渲染TabA的数据,TabB请求返回,覆盖TabA的数据,会造成页面是TabA,但是数据确实TabB的数据。
这种情况是超出我们的预期的,且行为是不受控制的。
处理
如果是在 React中,有几种处理这种竞态请求问题的方式
状态设计&强制卸载
在组件设计之处就要考虑到竞态请求的情况,将接口和状态封装在组件内部,这里的组件就类似于我们上面的 TabA
、TabB
,当切换Tab时强制卸载掉组件
这样可以保证Tab在切换时,会卸载掉无用的组件,避免竞态请求造成的情况发生,但是受限于业务功能,且对性能会有一定的影响。
请求判断拦截
第二种方法是对请求成功后添加判断的方式,来决定是不是当前组件所对应的数据
上面的方案是根据请求参数的Id来判断,同样在没有ID的时候,我们还可以对比请求的Url
或者自定义ID
来进行判断
利用 useEffect destroy 函数修改状态
使用 AbortController 中断请求
AbortController 接口表示一个控制器对象,允许你根据需要中止一个或多个 Web 请求。
你可以使用 AbortController.AbortController() 构造函数创建一个新的 AbortController。使用 AbortSignal 对象可以完成与 DOM 请求的通信。
在fetch
中如下使用
在 axios
中使用
Axios最开始使用 cancalToken 来实现取消请求的功能,但是在 v0.22 版本之后官方就不再建议使用cancelToken
了,推荐与fetch
一样使用 AbortController
参考