如何解决useEffect首次渲染时同时执行两个函数无数据问题
问题分析与解决方案
你的问题核心在于闭包捕获旧状态+异步更新的竞态覆盖:
- 由于useEffect使用空依赖数组,内部的
values变量会一直保持组件首次渲染时的初始值。 getCategory和getPublisher是异步请求,它们完成的顺序不确定,后完成的请求执行setValues({ ...values, ... })时,会基于初始的values重新构建状态对象,直接覆盖先完成请求的更新结果(比如先完成的请求刚把categories设好,后完成的请求就用初始值覆盖,导致categories变回初始状态)。
解决方法1:使用函数式状态更新(推荐)
React的setState支持传入函数,函数会接收最新的状态值作为参数,这样就能确保每次更新都是基于当前最新的状态,不会被闭包的旧值影响:
useEffect(() => { const getCategory = () => { allcategory().then((res) => { if (res.data.error) { console.log("error"); setValues(prev => ({ ...prev, message: res.data.error })); } else { setValues(prev => ({ ...prev, categories: res.data.category, formData: new FormData(), })); } }); }; const getPublisher = () => { allpublisher().then((res) => { if (res.data.error) { console.log("error"); setValues(prev => ({ ...prev, message: res.data.error })); } else { setValues(prev => ({ ...prev, publishers: res.data.publisher, formData: new FormData(), })); } }); }; getCategory(); getPublisher(); }, []);
解决方法2:合并异步请求,一次性更新状态
如果两个请求没有依赖关系,可以用Promise.all同时发起,等待全部请求完成后再统一更新状态,避免多次状态更新的竞态:
useEffect(() => { Promise.all([allcategory(), allpublisher()]) .then(([categoryRes, publisherRes]) => { const newValues = { ...values }; let hasError = false; if (categoryRes.data.error) { console.log("category error"); newValues.message = categoryRes.data.error; hasError = true; } else { newValues.categories = categoryRes.data.category; } if (!hasError && publisherRes.data.error) { console.log("publisher error"); newValues.message = publisherRes.data.error; hasError = true; } else if (!hasError) { newValues.publishers = publisherRes.data.publisher; } newValues.formData = new FormData(); setValues(newValues); }) .catch(err => { console.log("request error", err); setValues(prev => ({ ...prev, message: "请求失败" })); }); }, []);
额外提示:如果formData不需要每次状态更新都重新创建,可以考虑用useRef存储它,避免不必要的实例化,减少性能开销。
内容的提问来源于stack exchange,提问作者sworup kc
相关产品推荐
相关产品推荐

