React State更新异常:新数据到来时显示旧数据或空数组
问题:useEffect中更新correctAnswers状态异常
在useEffect内调用updateCorrectAnswers函数时,correctAnswers状态存在异常:初始显示为空数组;发起新API请求获取数据后,状态却显示旧数据。相关代码如下:
function App() { const [trivias, setTrivias] = useState(null); const [isFetched, setIsFetched] = useState(false); const [correctAnswers, setCorrectAnswers] = useState([]); const updateCorrectAnswers = () => { trivias.map((trivia) => { setCorrectAnswers((prev) => [...prev, trivia.correct_answer]); }); }; async function dataFetch() { const res = await fetch( "https://opentdb.com/api.php?amount=5&type=multiple" ); const data = await res.json(); return setTrivias(data.results); } const shuffle = (trivia, index) => { const answers = [...trivia.incorrect_answers, trivia.correct_answer]; const shuffledAnswers = getRandomAnswers(answers); return shuffledAnswers; }; const getRandomAnswers = (answers) => { const newAnswers = []; while (newAnswers.length !== 4) { const getRandomIndex = Math.floor(Math.random() * 4); const condition = newAnswers.some( (item) => item === answers[getRandomIndex] ); if (!condition) { newAnswers.push(answers[getRandomIndex]); } } return newAnswers; }; const getTrivias = () => { return trivias.map((trivia, index) => { return <Trivia question={trivia.question} answers={shuffle(trivia)} />; }); }; useEffect(() => { dataFetch(); }, []); useEffect(() => { if (!!trivias) { setIsFetched(true); updateCorrectAnswers(); console.log({ correctAnswers }); } }, [trivias]); if (!isFetched) { return <h1>loading</h1>; } return ( <div className="App"> <div>{getTrivias()}</div> <div className="check"> <button>Check Answers</button> </div> </div> ); } export default App;
问题原因
- 状态更新异步特性:React的
setState是异步操作,调用updateCorrectAnswers后立刻console.log(correctAnswers),此时获取的是更新前的旧状态,因为状态更新还未完成。 - 多次setState导致的异常:
updateCorrectAnswers中通过map循环多次调用setCorrectAnswers,每次异步更新都会依赖当时的prev状态,循环中的多次异步更新会导致状态累积逻辑混乱,且效率低下。 - 闭包陷阱:监听
trivias的useEffect中,correctAnswers被闭包捕获的是初始值,所以直接打印无法看到最新状态。
修复方案
1. 一次性生成正确答案数组
修改updateCorrectAnswers,直接生成完整的正确答案数组后一次性更新状态,避免多次调用setState:
const updateCorrectAnswers = () => { const newCorrectAnswers = trivias.map(trivia => trivia.correct_answer); setCorrectAnswers(newCorrectAnswers); };
2. 正确监听状态更新结果
如果需要确认状态更新后的结果,不要在调用setState后立刻打印,新增一个监听correctAnswers的useEffect:
useEffect(() => { console.log("更新后的correctAnswers:", correctAnswers); }, [correctAnswers]);
3. 简化逻辑,合并状态更新
可以在dataFetch中直接处理正确答案,减少额外的useEffect依赖:
async function dataFetch() { const res = await fetch("https://opentdb.com/api.php?amount=5&type=multiple"); const data = await res.json(); setTrivias(data.results); // 直接生成并更新正确答案数组 setCorrectAnswers(data.results.map(trivia => trivia.correct_answer)); setIsFetched(true); }
同时移除原监听trivias的useEffect内的updateCorrectAnswers调用,简化整体逻辑。
内容的提问来源于stack exchange,提问作者Emin Çoşkun
相关产品推荐
相关产品推荐

