React中如何避免变量在组件每次重渲染时被重新初始化
核心问题定位
你在App组件函数内部用let定义了questions数组,每次组件重渲染时,整个函数体都会从头执行,重新把数组赋值为内置的初始题目列表——不管之前在checkAnswer里对duration做过什么修改,只要触发重渲染(比如每秒执行的setQuizTime),数组就会被重置为每个题duration默认15的初始状态。
修正方向
- 不要把需要持久保存的可变数据写在组件内部的普通变量里,这类变量的生命周期和组件函数的执行绑定,每次重渲染都会被重新初始化。
- 用
useState管理questions数组的状态,这类状态会被React保存,跨重渲染不会丢失。 - 所有对题目属性的修改,都要通过
setQuestions生成新的状态对象完成,不要直接修改原数组里的对象属性——既符合React的状态追踪规则,也能避免可变引用带来的状态混乱。 - 如果需要让题目列表全局可访问,只要把这个由
useState管理的状态放到上层的Context Provider里,或者用独立的全局状态库托管,就可以实现跨组件读取,不会影响状态的持久保存。
顺带修正的原有代码问题
- 原
useEffect的清理逻辑错误:之前在clearTimeout里传入的是quizTime的数值,不是定时器的实际ID,会导致定时器无法正确清理,出现内存泄漏。 - 原
findMaxQuestion的遍历逻辑有bug:初始max为undefined时的分支判断会导致最大值对比失效,简化为reduce实现可以避免这个问题。 - 原代码没有在初始题目对象上显式定义
duration属性,要靠findMaxQuestion动态补默认值,直接在初始题目配置里定义duration:15更清晰。
修正后的参考代码
// 把初始题目数组移到组件外部,只初始化一次,避免每次重渲染重新生成 const initialQuestions = [ { questionText: 'What is the capital of France?', answerOptions: [ { answerText: 'New York', isCorrect: false }, { answerText: 'London', isCorrect: false }, { answerText: 'Paris', isCorrect: true }, { answerText: 'Dublin', isCorrect: false }, ], duration: 15 }, { questionText: 'Who is CEO of Tesla?', answerOptions: [ { answerText: 'Jeff Bezos', isCorrect: false }, { answerText: 'Elon Musk', isCorrect: true }, { answerText: 'Bill Gates', isCorrect: false }, { answerText: 'Tony Stark', isCorrect: false }, ], duration: 15 }, { questionText: 'The iPhone was created by which company?', answerOptions: [ { answerText: 'Apple', isCorrect: true }, { answerText: 'Intel', isCorrect: false }, { answerText: 'Amazon', isCorrect: false }, { answerText: 'Microsoft', isCorrect: false }, ], duration: 15 }, { questionText: 'How many Harry Potter books are there?', answerOptions: [ { answerText: '1', isCorrect: false }, { answerText: '4', isCorrect: false }, { answerText: '6', isCorrect: false }, { answerText: '7', isCorrect: true }, ], duration: 15 }, ]; function App() { // 用useState管理题目列表,状态跨重渲染持久保留 const [questions, setQuestions] = useState(initialQuestions); const [quizTime, setQuizTime] = useState(90) // in seconds useEffect(() => { if (quizTime <= 0) return; // 保存定时器ID,清理函数清除正确的ID const timerId = setTimeout(() => setQuizTime(prevTime => prevTime - 1), 1000); return () => clearTimeout(timerId); }, [quizTime]); function checkAnswer(answer, question, time) { if (answer == null) return; const longerThanHalf = question.duration - time > question.duration / 2; let newDuration = question.duration; if (answer.isCorrect) { newDuration = longerThanHalf ? Math.max(question.duration - 2, 5) : Math.max(question.duration - 1, 5); } // 生成新的状态数组更新,不直接修改原对象 setQuestions(prevQuestions => prevQuestions.map(q => q.questionText === question.questionText ? { ...q, duration: newDuration } : q )); }; const findMaxQuestion = () => { // 简化最大值查找逻辑,避免初始值判断错误 return questions.reduce((max, current) => current.duration > max.duration ? current : max , questions[0]); } if (quizTime <= 0) { return <div>测验结束</div> } const maxQuestion = findMaxQuestion(); return ( <div className="container"> <div>{quizTime} seconds left</div> <FlashCard question = {maxQuestion} checkAnswer = {checkAnswer}/> </div> ); }
内容的提问来源于stack exchange,提问作者aspiringsomeone
相关产品推荐
相关产品推荐

