React useEffect中使用async/Promise时setState异步取值为空问题
问题原因
- React 函数组件里
useState返回的状态更新函数不是Promise对象,前面加await不会产生等待效果,状态更新是批量异步调度的,不会在当前执行上下文里立刻完成。 - 函数组件每一次渲染都会生成独立的闭包,你在
fetchQuestions函数里最后返回的questionsArray,永远是当前渲染闭包中保存的初始值(也就是定义状态时设置的初始值null/空数组),哪怕后续状态更新触发重渲染,当前闭包里的这个变量也不会发生变化。 - useEffect初次执行时拿到的
questionsArray也是首次渲染闭包里的初始值,你在then回调里判断questionsArray.length,本质是在判断永远不会变的初始值,自然拿不到有效结果。
修复代码
直接在数据处理阶段拿到最终结果传递,不要绕一圈等状态更新后再从闭包里取值:
- 改造
fetchQuestions函数,提前计算好处理后的数据,直接返回计算结果,不要返回闭包内的状态,也不需要给setter加await:
const fetchQuestions = async () => { const data = require("../../question.json"); // 提前处理两份需要存入状态的数据 const questionList = data.map((item: IQuestion) => ({ id: item.id, question: item.question, answers: item.answers })); const questionArrayList = data.map((item: IQuestion) => ({ id: item.id, name: item.question })); // 触发状态更新,不需要await setQuestions(questionList); setQuestionsArray(questionArrayList); // 直接返回处理好的数组,不要从状态里取值 return questionArrayList; }
- 调整useEffect逻辑,直接使用Promise返回的处理结果做判断,不要依赖闭包内的旧状态:
useEffect(() => { if (nick.length < 1) { navigate('/quiz'); return; } fetchQuestions().then((res)=>{ if (res.length > 0){ handleQuest(res); } else { console.log("Nothing") } }) }, []);
补充说明:如果后续场景必须要等状态更新完成后再执行逻辑,不要给setter加await,应该单独新增一个依赖对应状态的useEffect监听状态变化,比如:
useEffect(() => { if (questionsArray.length > 0) { handleQuest(questionsArray) } }, [questionsArray])但当前场景不需要这么写,数据处理阶段已经拿到了最终值,直接传递即可,多写监听useEffect反而会产生冗余执行。
内容的提问来源于stack exchange,提问作者Lagzord
相关产品推荐
相关产品推荐

