You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React页面刷新时如何让useEffect重新执行及解决状态undefined问题

解决React页面刷新后状态值变为undefined的问题

核心问题分析

  1. 状态更新异步导致取值错误:generateRandomNumber里的setRandom是异步操作,调用后立刻在nextQuestion里访问random拿到的还是旧值,导致questions[random]取不到正确数据,刷新后该问题被放大。
  2. useEffect依赖逻辑错误:原代码用firstRender作为依赖,会触发无限循环(useEffect内调用setFirstRender(true)改变依赖,进而重新执行),且未考虑questions是否加载完成。
  3. 未做空值容错处理:刷新后组件重新挂载,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 10:45:47