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

React中如何搭建分步问答表单并实现答题框切换?

解决方案

1. 重构数据结构,关联标题与选项

把分散的标题数组和选项数组整合成结构化数组,每个元素对应一道题的标题和选项,通过counter就能直接同步获取当前题的完整信息,彻底解决数组索引不匹配的问题:

// 结构化题目数据:每一项包含标题和对应的选项
const questions = [
  { title: "title1", options: ["answer 1", "answer 2", "answer 3", "answer 4", "answer 5", "answer 6"] },
  { title: "title2", options: ["answer 1", "answer 2", "answer 3", "answer 4", "answer 5", "answer 6"] },
  { title: "title3", options: ["answer 1", "answer 2", "answer 3", "answer 4", "answer 5", "answer 6"] },
  { title: "title4", options: ["answer 1", "answer 2", "answer 3", "answer 4", "answer 5", "answer 6"] }
];

2. 简化状态管理

不需要单独维护title和qBoxes状态,直接通过counter从questions数组中提取当前题信息,减少冗余状态:

function ReportForm(props) {
  // 题目计数器,初始指向第0题
  const [counter, setCounter] = useState(0);
  // 存储所有题的答案:数组索引对应题目序号,值为该题选中的选项数组
  const [answers, setAnswers] = useState(Array(questions.length).fill([]));

  // ... 后续逻辑
}

3. 完善答题选中与收集逻辑

  • 处理选项的选中/取消状态,按题目序号存储答案,确保切换题目后保留之前的选择
  • 避免直接修改原数组(如pop),采用React推荐的不可变更新方式:
const handleClick = (event) => {
  const optionValue = event.currentTarget.value;
  const currentQuestionAnswers = answers[counter];
  let updatedAnswers;

  if (event.currentTarget.classList.contains('active')) {
    // 取消选中:过滤掉当前选项
    updatedAnswers = currentQuestionAnswers.filter(val => val !== optionValue);
    event.currentTarget.classList.remove('active');
  } else {
    // 选中:添加当前选项
    updatedAnswers = [...currentQuestionAnswers, optionValue];
    event.currentTarget.classList.add('active');
  }

  // 更新对应题目的答案
  const newAnswers = [...answers];
  newAnswers[counter] = updatedAnswers;
  setAnswers(newAnswers);
};

4. 修正Next/Previous按钮逻辑

处理边界条件(第一题/最后一题),避免无效操作,同时优化状态更新方式:

const submitNext = () => {
  if (counter < questions.length - 1) {
    setCounter(prev => prev + 1);
  }
};

const submitPrev = () => {
  if (counter > 0) {
    setCounter(prev => prev - 1);
  }
};

5. 渲染时自动恢复选中状态

切换回之前的题目时,自动恢复该题已选中选项的样式:

return (
  <div>
    {/* 显示当前题的标题 */}
    <p>{questions[counter].title}</p>
    {/* 渲染当前题的选项,自动恢复选中状态 */}
    {questions[counter].options.map((option) => (
      <button
        key={option} // 添加唯一key,符合React渲染规范
        onClick={handleClick}
        className={`boxStyle ${answers[counter].includes(option) ? 'active' : ''}`}
        name="contentYes"
        value={option}
      >
        {option}
      </button>
    ))}

    <button 
      className="buttonNext" 
      onClick={submitNext}
      disabled={counter === questions.length - 1} // 最后一题时禁用Next
    >Next</button>
    <button 
      className="buttonPrev" 
      onClick={submitPrev}
      disabled={counter === 0} // 第一题时禁用Previous
    >Previous</button>
  </div>
);

完整修正代码

import { useState } from 'react';

// 结构化题目数据
const questions = [
  { title: "title1", options: ["answer 1", "answer 2", "answer 3", "answer 4", "answer 5", "answer 6"] },
  { title: "title2", options: ["answer 1", "answer 2", "answer 3", "answer 4", "answer 5", "answer 6"] },
  { title: "title3", options: ["answer 1", "answer 2", "answer 3", "answer 4", "answer 5", "answer 6"] },
  { title: "title4", options: ["answer 1", "answer 2", "answer 3", "answer 4", "answer 5", "answer 6"] }
];

function ReportForm(props) {
  const [counter, setCounter] = useState(0);
  // 初始化答案数组,每个元素对应一道题的选中选项
  const [answers, setAnswers] = useState(Array(questions.length).fill([]));

  const handleClick = (event) => {
    const optionValue = event.currentTarget.value;
    const currentAnswers = answers[counter];
    let updatedCurrentAnswers;

    if (event.currentTarget.classList.contains('active')) {
      updatedCurrentAnswers = currentAnswers.filter(val => val !== optionValue);
      event.currentTarget.classList.remove('active');
    } else {
      updatedCurrentAnswers = [...currentAnswers, optionValue];
      event.currentTarget.classList.add('active');
    }

    // 不可变更新答案数组
    const newAnswers = [...answers];
    newAnswers[counter] = updatedCurrentAnswers;
    setAnswers(newAnswers);
  };

  const submitNext = () => {
    if (counter < questions.length - 1) {
      setCounter(prev => prev + 1);
    }
  };

  const submitPrev = () => {
    if (counter > 0) {
      setCounter(prev => prev - 1);
    }
  };

  return (
    <div>
      <p>{questions[counter].title}</p>
      {questions[counter].options.map((option) => (
        <button
          key={option}
          onClick={handleClick}
          className={`boxStyle ${answers[counter].includes(option) ? 'active' : ''}`}
          name="contentYes"
          value={option}
        >
          {option}
        </button>
      ))}

      <button 
        className="buttonNext" 
        onClick={submitNext}
        disabled={counter === questions.length - 1}
      >Next</button>
      <button 
        className="buttonPrev" 
        onClick={submitPrev}
        disabled={counter === 0}
      >Previous</button>
    </div>
  );
}

export default ReportForm;

核心改进说明

  • 数据结构化:彻底解决标题与选项数组不同步的问题,通过counter直接定位当前题的所有信息
  • 答案按题存储:避免不同题的答案混淆,切换题目后能完全保留之前的选择状态
  • 不可变状态更新:遵循React状态更新规范,避免直接修改原数组导致的状态异常
  • 边界交互优化:禁用边界状态的按钮,提升用户操作体验
  • 自动恢复选中样式:切换回旧题目时,自动恢复之前选中的选项样式

内容的提问来源于stack exchange,提问作者nordan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 01:10:34