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

如何为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 01:05:01