React页面刷新时如何让useEffect重新执行及解决状态undefined问题
解决React页面刷新后状态值变为undefined的问题
核心问题分析
- 状态更新异步导致取值错误:
generateRandomNumber里的setRandom是异步操作,调用后立刻在nextQuestion里访问random拿到的还是旧值,导致questions[random]取不到正确数据,刷新后该问题被放大。 - useEffect依赖逻辑错误:原代码用
firstRender作为依赖,会触发无限循环(useEffect内调用setFirstRender(true)改变依赖,进而重新执行),且未考虑questions是否加载完成。 - 未做空值容错处理:刷新后组件重新挂载,
questions可能还未从Context加载完成,直接访问会触发undefined报错。
修复步骤
1. 修复nextQuestion的异步状态问题
无需将随机数存入state,直接在函数内生成并使用,规避异步更新的延迟:
const nextQuestion = () => { if (!questions || questions.length === 0) return; // 直接生成随机数并获取对应题目 const rand = Math.floor(Math.random() * questions.length); const q = questions[rand]; if (!q) return; // 使用函数式更新获取最新状态值 setQuestionNum(prev => prev + 1); setQuestionsAnswered(prev => prev + 1); setQuestionImageId(q.gameId); setQuestionText(q.question); setQuestionOptions(q.options); setQuestionCorrect(q.correct); }
同时删除random相关state和generateRandomNumber函数,简化逻辑。
2. 调整useEffect逻辑,确保挂载(刷新)时执行
修改useEffect,让它在组件首次挂载(包括刷新后)执行,同时依赖questions,确保数据加载完成后再初始化:
// 删除firstRender相关的state和逻辑 useEffect(() => { // 仅当questions有数据时执行初始化 if (questions?.length > 0) { nextQuestion(); } }, [questions]); // 依赖questions,数据加载完成时触发
3. 渲染层增加容错处理
在JSX中使用可选链操作符?.,避免状态未初始化时访问属性报错:
<div className="quiz-options"> <Link to="/"><button className="btn small-med-btn quiz-option-btn" id="quiz-option-1">{questionOptions?.[0] || ''}</button></Link> <Link to="/"><button className="btn small-med-btn quiz-option-btn" id="quiz-option-2">{questionOptions?.[1] || ''}</button></Link> <Link to="/"><button className="btn small-med-btn quiz-option-btn" id="quiz-option-3">{questionOptions?.[2] || ''}</button></Link> </div>
同时在组件开头添加加载状态判断:
if (!questions || questions.length === 0) { return <div className="text-align-center">Loading...</div>; }
4. 优化状态初始化
将状态初始值改为更安全的默认值,避免空值报错:
const [questionOptions, setQuestionOptions] = useState<string[]>([]); const [questionText, setQuestionText] = useState<string>(""); const [questionImageId, setQuestionImageId] = useState<number>(0);
完整修复后的组件代码
const Quiz_Play = () => { const { questions } = useContext(QuizContext) as IQuizContext; const [questionNum, setQuestionNum] = useState<number>(1); const [questionText, setQuestionText] = useState<string>(""); const [questionImageId, setQuestionImageId] = useState<number>(0); const [questionOptions, setQuestionOptions] = useState<string[]>([]); const [questionCorrect, setQuestionCorrect] = useState<string>(""); const [questionsAnswered, setQuestionsAnswered] = useState<number>(0); const [correctAnswers, setCorrectAnswers] = useState(0); const nextQuestion = () => { if (!questions || questions.length === 0) return; const rand = Math.floor(Math.random() * questions.length); const q = questions[rand]; if (!q) return; setQuestionNum(prev => prev + 1); setQuestionsAnswered(prev => prev + 1); setQuestionImageId(q.gameId); setQuestionText(q.question); setQuestionOptions(q.options); setQuestionCorrect(q.correct); }; useEffect(() => { if (questions?.length > 0) { nextQuestion(); } }, [questions]); // 加载状态提示 if (!questions || questions.length === 0) { return <div className="text-align-center">Loading...</div>; } return ( <div> <Logo /> <div className="wrapper"> <h1 className="quizInfo_h1"> Question {questionNum} </h1> <h2 className="quizQuestion_h2 text-align-center">{questionText || "Loading question..."}</h2> <p className="correctCounter_p text-align-center no-margin "> <span style={{ color: '#f2c112' }}>{correctAnswers}</span>/{questionsAnswered} </p> <div className="quizImage"> {questionImageId !== 0 && <Game_ById id={questionImageId} />} </div> </div> <div className="quiz-options"> <Link to="/"> <button className="btn small-med-btn quiz-option-btn" id="quiz-option-1"> {questionOptions?.[0] || ""} </button> </Link> <Link to="/"> <button className="btn small-med-btn quiz-option-btn" id="quiz-option-2"> {questionOptions?.[1] || ""} </button> </Link> <Link to="/"> <button className="btn small-med-btn quiz-option-btn" id="quiz-option-3"> {questionOptions?.[2] || ""} </button> </Link> </div> <p style={{ marginTop: 10, color: "rgba(255, 255, 255, 0.5)" }} className="text-align-center"> Don't want to play anymore? Feeling like a <Link to="/quiz" className="yellowlink">coward</Link>? </p> </div> ); }; export default Quiz_Play;
内容的提问来源于stack exchange,提问作者nostrad0muz
相关产品推荐
相关产品推荐

