React Native:useEffect组件首次渲染未执行的问题排查与修复
我在React Native代码中使用useEffect避免“Too many re-renders”错误,预期PracticeTest组件首次运行时useEffect会执行,但实际完全未运行,导致questionAnswers数组为空。已添加console.log验证useEffect未执行,寻求修复方法。
相关代码:
function PracticeTest({setCurrentMode}){ const [index, setIndex] = useState(0); const [selected, setSelected] = useState(0); const [feedback, setFeedback] = useState(''); const [score, setScore] = useState(0); // Create 'isCorrect' var to designate if ready to move on to the next question const [isCorrect, setIsCorrect] = useState(false); // get only the questions needed testQuestions = require('./testQuestions.json'); let questionAnswers = []; let firstQuestionIndex; let lastQuestionIndex; let testLength = lastQuestionIndex - firstQuestionIndex; const [shuffled, setShuffled] = useState(false); const [done, setDone] = useState(false); useEffect(() => { questionAnswers = []; for(var i=0; i<testQuestions.length; i++){ if((i>=firstQuestionIndex) && (i< lastQuestionIndex)){ questionAnswers.push(testQuestions[i]); } } shuffle(questionAnswers); testLength = 10; shuffled = true; }); // if quizType is "practiceTest" then the questions will be randomly selected and jumbled // Source for shuffle function (modified by me): https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array function shuffle(array) { if(shuffled){ console.log("ALREADY SHUFFLEDDDDDDDDDDDDDD"); } else{ let currentIndex = array.length; // While there remain elements to shuffle... while (currentIndex != 0) { // Pick a remaining element... let randomIndex = Math.floor(Math.random() * currentIndex); currentIndex--; // And swap it with the current element. [array[currentIndex], array[randomIndex]] = [ array[randomIndex], array[currentIndex]]; } console.log("Shuffle!!"); } } let question = questionAnswers[index].question; function check(){ if(selected == questionAnswers[index].correctID){ // add 1 point to score if the feedback is blank if(feedback==''){ setScore(score+1); } setFeedback('Correct!'); setIsCorrect(true); } else{ setFeedback("Try again"); } } function proceed() { if((index + 1) >= testLength){ // Code to be executed if done setDone(true); } else{ setIndex(index + 1); setIsCorrect(false); setColor1('#FF6868'); setColor2('#FF6868'); setColor3('#FF6868'); setColor4('#FF6868'); setFeedback(''); } } const [color1, setColor1] = useState('#FF6868'); const [color2, setColor2] = useState('#FF6868'); const [color3, setColor3] = useState('#FF6868'); const [color4, setColor4] = useState('#FF6868'); return ( <View> {done ? <QuizDone setCurrentMode={setCurrentMode} score={score} testLength={testLength}/> : <View style={styles.app}> <Text style={styles.logo}>Testing your Knowledge</Text> <Text style={styles.question}>{question}</Text> <AnswerChoices setSelected={setSelected} index={index} questionAnswers={questionAnswers} color1={color1} color2={color2} color3={color3} color4={color4} setColor1={setColor1} setColor2={setColor2} setColor3={setColor3} setColor4={setColor4} /> <BackButton setCurrentMode={setCurrentMode}></BackButton> {/** Feedback */} <View style={{ position: 'absolute', width: 144, height: 42, top: 595, left: 63, justifyContent: 'center', }}> <Text>{feedback}</Text> </View> {/** Check Answer */} { isCorrect ? <TouchableOpacity style={styles.checkButton} onPress={proceed}> <Text style={{color: 'white', alignSelf: 'center',}}>Move on</Text> </TouchableOpacity> : <TouchableOpacity style={styles.checkButton} onPress={check}> <Text style={{color: 'white', alignSelf: 'center',}}>Check</Text> </TouchableOpacity> } </View> } </View> ) }
我尝试添加console.log验证useEffect是否运行,但无输出,确认useEffect内代码未执行,导致questionAnswers数组无值。
1. 给useEffect添加空依赖数组
当前useEffect未传递依赖数组,会导致它在每次组件渲染时都执行,但组件渲染时直接访问questionAnswers[index].question,此时数组为空会抛出错误,组件渲染失败,useEffect根本没机会执行。
给useEffect加上[]作为第二个参数,让它仅在组件挂载时执行一次:
useEffect(() => { // 你的逻辑 }, []);
2. 将questionAnswers、testLength转为状态变量
你使用普通变量let questionAnswers = []和let testLength,组件渲染时这些变量会被重新初始化,修改它们也不会触发组件更新。需要用useState转为状态:
// 替换原来的let声明 const [questionAnswers, setQuestionAnswers] = useState([]); const [testLength, setTestLength] = useState(0);
在useEffect中更新状态:
useEffect(() => { const filteredQuestions = []; // 给first/lastQuestionIndex设置实际值,示例设为0和10 const first = 0; const last = 10; for(let i=0; i<testQuestions.length; i++){ if(i >= first && i < last){ filteredQuestions.push(testQuestions[i]); } } // 直接在useEffect内处理洗牌逻辑 const shuffledQuestions = [...filteredQuestions]; let currentIndex = shuffledQuestions.length; while (currentIndex != 0) { let randomIndex = Math.floor(Math.random() * currentIndex); currentIndex--; [shuffledQuestions[currentIndex], shuffledQuestions[randomIndex]] = [ shuffledQuestions[randomIndex], shuffledQuestions[currentIndex]]; } setQuestionAnswers(shuffledQuestions); setTestLength(10); setShuffled(true); }, []);
3. 初始化firstQuestionIndex和lastQuestionIndex
当前代码中这两个变量未赋值,会导致过滤条件i>=undefined永远为false,过滤不出任何问题。必须给它们设置具体初始值,比如从props传入或在useEffect内定义。
4. 避免直接修改状态变量shuffled
你在useEffect中直接写shuffled = true不会更新组件状态,必须用setShuffled(true)修改。
5. 防止数组为空时访问question
渲染question前先判断数组是否有数据,避免报错:
if (questionAnswers.length === 0) { return <Text>Loading...</Text>; } const question = questionAnswers[index].question;
内容的提问来源于stack exchange,提问作者user25911215

