useEffect空依赖数组修改isLoading状态触发无限循环问题
问题原因
你贴出的slice代码本身没有逻辑问题,state.isLoading = false这行只是触发状态更新的节点,无限循环的根因在组件渲染与副作用的执行逻辑,绝大多数场景是以下两种原因:
- 原因1:通过isLoading控制了发请求组件的条件渲染
你大概率在父组件中写了类似{!isLoading && <SearchForm />}的条件渲染逻辑,且isLoading初始值为false,执行链路如下:- 页面初始加载,isLoading为false,SearchForm组件挂载,useEffect触发,dispatch(fetchMovies)发起请求
- thunk进入pending状态,isLoading被设为true,父组件判定不满足渲染条件,直接卸载SearchForm
- 请求完成进入fulfilled分支,isLoading被改回false,父组件重新挂载SearchForm
- 新挂载的SearchForm再次触发useEffect发请求,回到步骤2,形成无限循环
- 原因2:useEffect依赖配置错误
如果你实际运行的代码里useEffect依赖数组不是空数组,比如误将isLoading、或者定义在组件内部的fetchMovies(每次渲染生成新引用)加入了依赖,执行链路如下:- 初始渲染时effect执行,dispatch发起请求,pending分支将isLoading设为true
- isLoading变化/新的fetchMovies引用生成,触发effect重执行,再次dispatch请求
- 请求完成fulfilled分支将isLoading改回false,再次触发effect执行,形成无限循环
解决方案
对应上述原因修复即可:
- 针对条件渲染问题
不要用isLoading控制承载请求逻辑的SearchForm组件的挂载卸载,改成只控制加载态UI的显示隐藏即可,示例写法:
如果必须条件渲染SearchForm,就把// 父组件 const isLoading = useSelector(state => state.movies.isLoading) return ( <div> <SearchForm /> {/* 仅控制加载提示的显示,不卸载业务组件 */} {isLoading && <div className="loading">电影列表加载中...</div>} </div> )dispatch(fetchMovies)的逻辑上移到不会被反复卸载的父组件中执行。 - 针对依赖配置问题
- 将
createAsyncThunk的定义移到组件外部,保证thunk函数引用全局稳定,不要写在组件函数内部 - useEffect的依赖数组不要放入isLoading这类会随请求流程变化的状态,Redux的dispatch函数本身引用稳定,空依赖数组或者仅放入dispatch都不会触发重复执行:
// 正确写法 useEffect(() => { dispatch(fetchMovies('Titanic')) }, [dispatch])
- 将
- 补充:如果是React 18严格模式下开发环境出现2次重复请求,不属于无限循环,是严格模式的挂载检查机制,生产环境不会出现,不需要特殊处理。
内容的提问来源于stack exchange,提问作者kamolmachine
相关产品推荐
相关产品推荐

