如何让React状态在首次重渲染时及时更新
问题描述
我从API获取词典数据,经过洗牌和过滤后在页面展示随机单词。当前代码能跑,但实现方式不规范:首次通过普通变量设置当前单词,后续才用状态变量。
如果只使用state,首次点击按钮触发重渲染时会出现currentPage未定义的错误,单词显示不出来,但后续操作都正常。我猜这和useState的异步特性、状态变更与渲染时机有关,试过用useEffect(代码里已经注释掉了)但没用,想找个更优雅的实现方案。
原代码
import React, { useState, useEffect } from "react"; import Word from "./components/Word"; const options = { method: "GET", headers: { "X-RapidAPI-Key": "fd76888365mshc55b027be74dc4dp1d4f7ejsn88ecdcc39f3f", "X-RapidAPI-Host": "lexicala1.p.rapidapi.com", }, }; const App = () => { const [entryCount, setEntryCount] = useState(0); const [pageCount, setPageCount] = useState(1); const [currentPage, setCurrentPage] = useState(""); const [currentWord, setCurrentWord] = useState(""); // useEffect(() => { // const word = document.querySelector('.word') // word.textContent = currentWord; // }); const shuffleResults = (array) => { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); const temp = array[i]; array[i] = array[j]; array[j] = temp; } return array; }; const filterResults = (words) => { words = words.results; const validPOS = ["noun", "verb", "adjective", "adverb"]; words = words.filter((word) => validPOS.includes(word.headword.pos)); return words; }; const getDictionary = async () => { const response = await fetch( `https://lexicala1.p.rapidapi.com/search-entries?source=global&language=sv&page=${pageCount}&page-length=30&sample=30&text=`, options ); let wordsData = await response.json(); wordsData = filterResults(wordsData); wordsData = shuffleResults(wordsData); setCurrentPage(wordsData); return wordsData; }; let page = ""; const generateWord = async () => { if (!currentPage) { page = await getDictionary(); setCurrentWord(page[entryCount].headword.text); } else setCurrentWord(currentPage[entryCount].headword.text); if (entryCount === currentPage.length - 1) { setPageCount(pageCount + 1); setEntryCount(0); setCurrentPage(""); } else setEntryCount(entryCount + 1); }; return ( <div className="App"> <button onClick={async () => { await generateWord(); }} > hey </button> <Word word={currentWord} /> <div className="word"></div> </div> ); }; export default App;
优化方案及代码
核心问题在于状态类型不一致(currentPage初始是空字符串,后续是数组)和闭包导致的状态过时,以下是修复后的优雅实现:
import React, { useState } from "react"; import Word from "./components/Word"; const options = { method: "GET", headers: { "X-RapidAPI-Key": "fd76888365mshc55b027be74dc4dp1d4f7ejsn88ecdcc39f3f", "X-RapidAPI-Host": "lexicala1.p.rapidapi.com", }, }; const App = () => { // currentPage初始化为空数组,保持类型一致 const [entryCount, setEntryCount] = useState(0); const [pageCount, setPageCount] = useState(1); const [currentPage, setCurrentPage] = useState([]); const [currentWord, setCurrentWord] = useState(""); const shuffleResults = (array) => { // 用解构避免修改原数组 const shuffled = [...array]; for (let i = shuffled.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; } return shuffled; }; const filterResults = (words) => { const validPOS = ["noun", "verb", "adjective", "adverb"]; return words.results.filter(word => validPOS.includes(word.headword.pos)); }; const getDictionary = async (pageNum) => { const response = await fetch( `https://lexicala1.p.rapidapi.com/search-entries?source=global&language=sv&page=${pageNum}&page-length=30&sample=30&text=`, options ); const wordsData = await response.json(); const filtered = filterResults(wordsData); return shuffleResults(filtered); }; const generateWord = async () => { // 统一处理数据获取逻辑 let targetPage = currentPage; if (targetPage.length === 0) { targetPage = await getDictionary(pageCount); setCurrentPage(targetPage); } // 设置当前单词 setCurrentWord(targetPage[entryCount].headword.text); // 用函数式更新避免闭包问题 setEntryCount(prev => { // 判断是否需要加载下一页 if (prev === targetPage.length - 1) { setPageCount(prevPage => prevPage + 1); setCurrentPage([]); return 0; } return prev + 1; }); }; return ( <div className="App"> <button onClick={generateWord}>获取随机单词</button> <Word word={currentWord} /> {/* 移除冗余的DOM节点,完全通过组件props传递数据 */} </div> ); }; export default App;
关键改动说明
- 统一状态类型:把
currentPage初始值设为[],避免空字符串和数组的类型混淆,解决首次加载时的未定义错误 - 避免闭包陷阱:使用
setEntryCount(prev => ...)这种函数式更新方式,确保获取的是最新的状态值 - 简化逻辑:移除普通变量
page,统一通过状态管理数据,避免混合状态和普通变量导致的不一致 - 纯函数优化:
shuffleResults改用解构数组避免修改原数据,保持函数纯性 - 冗余代码清理:移除手动操作DOM的useEffect和多余的
.word节点,完全通过React的props传递数据实现渲染
内容的提问来源于stack exchange,提问作者jakemadash
相关产品推荐
相关产品推荐

