React JS中MongoDB数据获取完成前的渲染错误修复:Quiz应用MCQ选项无法读取问题
解决Quiz应用中MCQ数据加载时的Undefined错误
我来帮你搞定这个问题——你遇到的是典型的React异步数据加载时序冲突:组件刚初始化时,MCQ请求依赖的requestedQuestion还没拿到有效数据(因为quizInfo还在从接口加载),导致发起了一次无效请求,进而让mcqInfo处于未就绪状态,这时候访问它的属性就会抛出Cannot read properties of undefined错误。咱们一步步拆解问题,然后给出解决方案:
问题根源拆解
- 组件初始化时,
quizInfo的初始状态里q_questions是空数组,所以requestedQuestion = quizQuestions[currentQuestion]等于undefined,这时候MCQ的useEffect会执行,用undefined作为ID去请求数据,要么返回无效结果,要么直接报错,导致mcqInfo变成错误状态(比如undefined)。 - 当
quizInfo加载完成后,quizQuestions更新,requestedQuestion变成有效的问题ID,MCQ的useEffect再次执行,这时候才会拿到正确的MCQ数据。 - 在这两次请求之间,你尝试访问
mcqInfo.m_alternatives,但此时mcqInfo要么是初始空状态,要么是错误的undefined,自然会触发报错。
解决方案:控制加载时序+空值保护
下面是修改后的完整代码,我会标注每一处关键修改的原因:
import React, { useEffect, useState } from "react"; import shuffleMcq from "../components/shuffleMcq"; const QuizTakePage = ({ match }) => { const reqUrl = match.params.path; // 获取URL中的Quiz ID const [quizInfo, setQuizInfo] = useState({ q_title: "", q_seo_description: "", q_questions: [], q_tags: [], m_subject: "", }); // 新增:标记Quiz数据是否加载完成 const [isQuizLoaded, setIsQuizLoaded] = useState(false); useEffect(() => { const fetchQuizData = async () => { try { const reqApiUrl = '/quiz/api/qid/' + reqUrl; const fetchedApiResult = await fetch(reqApiUrl); const resultJson = await fetchedApiResult.json(); setQuizInfo(resultJson); setIsQuizLoaded(true); // Quiz数据加载完成后标记状态 } catch (error) { console.error("加载Quiz数据失败:", error); } }; fetchQuizData(); }, [reqUrl]); // 依赖reqUrl,确保URL变化时重新请求Quiz数据 // Quiz数据提取 const quizTitle = quizInfo.q_title; const quizDesc = quizInfo.q_seo_description; const quizQuestions = quizInfo.q_questions; // MCQ相关状态 const [currentQuestion, setCurrentQuestion] = useState(0); const [mcqInfo, setMcqInfo] = useState({ m_title: "", m_question: "", m_alternatives: [], m_language: "", }); // 新增:标记MCQ数据是否加载完成 const [isMcqLoaded, setIsMcqLoaded] = useState(false); useEffect(() => { // 新增:只有当Quiz加载完成,且当前问题ID有效时,才发起MCQ请求 if (!isQuizLoaded || !quizQuestions[currentQuestion]) return; const fetchApiResponse = async () => { try { setIsMcqLoaded(false); // 开始加载MCQ前标记状态 const requestedQuestion = quizQuestions[currentQuestion]; const reqApiUrl = '/mcq/api/mid/' + requestedQuestion; const fetchedApiResult = await fetch(reqApiUrl); const resultJson = await fetchedApiResult.json(); setMcqInfo(resultJson); setIsMcqLoaded(true); // MCQ加载完成后标记状态 } catch (error) { console.error("加载MCQ数据失败:", error); setIsMcqLoaded(true); // 即使请求失败,也标记加载完成避免无限循环 } }; fetchApiResponse(); }, [isQuizLoaded, currentQuestion, quizQuestions]); // 依赖关键状态,确保数据变化时重新请求 // 新增:空值保护,用可选链避免访问undefined的属性 const mcqQuestion = mcqInfo?.m_question || "加载中..."; const mcqOptions = mcqInfo?.m_alternatives || []; console.log(mcqOptions); return ( <> {/* 新增:Quiz加载中显示提示 */} {!isQuizLoaded ? ( <div>加载Quiz信息中...</div> ) : ( <> <h1>{quizTitle}</h1> <p>{quizDesc}</p> {/* 新增:MCQ加载中显示提示 */} {!isMcqLoaded ? ( <div>加载题目中...</div> ) : ( <div> <h3>{mcqQuestion}</h3> <ul> {mcqOptions.map((option, index) => ( <li key={index}>{option}</li> ))} </ul> </div> )} </> )} </> ); }; export default QuizTakePage;
关键修改说明
- 加载状态标记:
isQuizLoaded和isMcqLoaded用来明确数据的加载状态,在加载过程中给用户显示友好提示,避免渲染未就绪的内容。 - 请求时机控制:MCQ的
useEffect里增加了判断,只有当Quiz数据加载完成且当前问题ID有效时,才发起MCQ请求,彻底避免用undefined作为ID的无效请求。 - 空值保护:使用可选链操作符
?.(比如mcqInfo?.m_alternatives),即使mcqInfo是undefined也不会报错,再通过||设置默认值,保证代码的健壮性。 - 错误处理:给
fetch添加try/catch,捕获请求过程中的错误,避免组件崩溃,同时方便调试问题。 - 依赖项优化:把
reqUrl加入Quiz请求的依赖项,确保URL变化时重新请求;MCQ请求的依赖项包含所有影响请求结果的状态,确保数据更新时能同步发起新请求。
这样修改后,不仅能解决你遇到的undefined错误,还能给用户提供更流畅的加载体验。
内容的提问来源于stack exchange,提问作者Aly
相关产品推荐
相关产品推荐

