如何为React自定义题型的测验应用优化数据结构?
React个性化测验应用的结构优化方案
问题背景
我正在开发一款基于React的测验应用,但遇到了结构优化的问题。网上的React测验示例大多是每个问题输入项数量、样式都一致的,但我的需求是每个问题都有不同的呈现形式。
目前我的代码能正常运行,但结构肯定可以更优,希望得到改进建议。
原始代码
import tools from '../assets/tools.png' import test from '../components/Questions' import { useState } from 'react' function SkillsCoach() { const [question, setQuestion] = useState(0) const [answers, setAnswers] = useState({}) // array to hold question numbers and respective functions const array = [ { Q: 0, Function: Start() }, { Q: 1, Function: Question1() }, { Q: 2, Function: Question2() } ] function Start() { return ( <div className="container"> <div className="row"> <div className="col-lg-12 text-center mx-auto mt-5"> <h1 className="fw-bold">Skills Coach</h1> </div> <div className="col-lg-4 text-center mx-auto mt-5"> <img src={tools} alt="" className='w-50' /> </div> </div> <div className="row"> <div className="col-sm-12 col-lg-10 text-center mx-auto mt-5" > <a className="btn btn-success w-50 fs-2" onClick={() => { setQuestion(question + 1) }} > Start </a> </div> </div> </div> ) } function Question1() { return ( <div className="container"> <div className="row"> <div className="col-lg-4 text-center mx-auto mt-5"> <img src={tools} alt="" className='w-50' /> </div> <div className="col-lg-12 text-center mx-auto mt-5"> <h1 className="fw-bold">What Emotion are you feeling?</h1> </div> </div> <div className="row"> <div className="col-sm-12 col-lg-10 text-center mx-auto mt-5" > {/* <a className="btn btn-danger w-50 fs-2" onClick={(e) => { setAnswers({ emotion: e.target.innerText }); setQuestion(question + 1) }} > Emotion </a> */} {mapElements()} </div> </div> </div> ) } function Question2() { return ( <div className="container"> <div className="row"> <div className="col-lg-12 text-center mx-auto mt-5"> <h1 className="fw-bold">Any Secondary Emotions?</h1> </div> <div className="col-lg-4 text-center mx-auto mt-5"> <img src={tools} alt="" className='w-50' /> </div> </div> <div className="row"> <div className="col-sm-12 col-lg-10 text-center mx-auto mt-5" > <a className="btn btn-warning w-50 fs-2" onClick={(e) => { console.log(answers.emotion) }} > More Emotions </a> </div> </div> </div> ) } function mapElements() { console.log(test) return ( test.emotions.map(emote => <a className="btn btn-danger w-50 fs-2 m-2" onClick={(e) => { setAnswers({ emotion: e.target.innerText }); setQuestion(question + 1) }} > {emote} </a>) ) } return ( <div> {array[question].Function} </div> ) } export default SkillsCoach
优化方案
1. 拆分独立组件
将每个问题/启动页拆分为独立的React组件,提升代码可维护性和复用性。
启动组件(StartScreen.jsx)
import tools from '../assets/tools.png'; export default function StartScreen({ onStart }) { return ( <div className="container"> <div className="row"> <div className="col-lg-12 text-center mx-auto mt-5"> <h1 className="fw-bold">Skills Coach</h1> </div> <div className="col-lg-4 text-center mx-auto mt-5"> <img src={tools} alt="Tools icon" className='w-50' /> </div> </div> <div className="row"> <div className="col-sm-12 col-lg-10 text-center mx-auto mt-5"> <button className="btn btn-success w-50 fs-2" onClick={onStart}> Start </button> </div> </div> </div> ); }
情绪选择问题组件(EmotionQuestion.jsx)
import tools from '../assets/tools.png'; import test from '../components/Questions'; export default function EmotionQuestion({ onAnswerSelect }) { const handleSelect = (emote) => { onAnswerSelect({ emotion: emote }); }; return ( <div className="container"> <div className="row"> <div className="col-lg-4 text-center mx-auto mt-5"> <img src={tools} alt="Tools icon" className='w-50' /> </div> <div className="col-lg-12 text-center mx-auto mt-5"> <h1 className="fw-bold">What Emotion are you feeling?</h1> </div> </div> <div className="row"> <div className="col-sm-12 col-lg-10 text-center mx-auto mt-5"> {test.emotions.map(emote => ( <button key={emote} className="btn btn-danger w-50 fs-2 m-2" onClick={() => handleSelect(emote)} > {emote} </button> ))} </div> </div> </div> ); }
次级情绪问题组件(SecondaryEmotionQuestion.jsx)
import tools from '../assets/tools.png'; export default function SecondaryEmotionQuestion({ answers }) { return ( <div className="container"> <div className="row"> <div className="col-lg-12 text-center mx-auto mt-5"> <h1 className="fw-bold">Any Secondary Emotions?</h1> </div> <div className="col-lg-4 text-center mx-auto mt-5"> <img src={tools} alt="Tools icon" className='w-50' /> </div> </div> <div className="row"> <div className="col-sm-12 col-lg-10 text-center mx-auto mt-5"> <button className="btn btn-warning w-50 fs-2" onClick={() => console.log(answers.emotion)}> More Emotions </button> </div> </div> </div> ); }
2. 重构主组件
使用配置数组管理测验流程,通过props传递状态更新逻辑,避免组件提前执行的问题。
import { useState } from 'react'; import StartScreen from './StartScreen'; import EmotionQuestion from './EmotionQuestion'; import SecondaryEmotionQuestion from './SecondaryEmotionQuestion'; function SkillsCoach() { const [currentStep, setCurrentStep] = useState(0); const [answers, setAnswers] = useState({}); // 测验流程配置:按顺序定义每个步骤的组件 const quizSteps = [ { component: StartScreen }, { component: EmotionQuestion }, { component: SecondaryEmotionQuestion } ]; const handleStart = () => { setCurrentStep(prev => prev + 1); }; const handleAnswerSelect = (newAnswer) => { // 使用函数式更新合并答案,避免覆盖已有数据 setAnswers(prev => ({ ...prev, ...newAnswer })); setCurrentStep(prev => prev + 1); }; const CurrentComponent = quizSteps[currentStep].component; return ( <div> <CurrentComponent onStart={handleStart} onAnswerSelect={handleAnswerSelect} answers={answers} /> </div> ); } export default SkillsCoach;
优化亮点
- 组件解耦:每个步骤独立成组件,便于单独修改和测试
- 流程可控:通过配置数组管理测验顺序,新增/调整步骤只需修改数组
- 状态安全:使用函数式更新合并答案,避免状态覆盖
- 性能优化:仅渲染当前步骤的组件,避免提前执行所有组件实例
内容的提问来源于stack exchange,提问作者MrBuchmas
相关产品推荐
相关产品推荐

