React实现问答组件切换上下题时持久化保留已选答案状态
React 问答应用题目切换+状态持久化实现
核心实现思路:
- 所有公共状态(当前题目序号、所有题的已选答案)统一放在父组件维护,避免子组件单独存状态导致切换卸载时数据丢失
- 同一时间只渲染当前序号对应的题目组件,不需要一次性加载全部题目
- 父组件通过props给子组件传递当前题的已选答案、更新答案的回调方法,子组件只负责渲染和触发选中事件
- 切换按钮做边界判断,避免超出题目序号范围
- 子组件渲染选项时,对比当前选项值和已传入选中值,匹配时给div添加
selected类名
第一步:修改父组件 Questions.js
引入useState做状态管理,改造后的代码如下:
import React, { useState } from "react"; import Qustionone from "./qustionone"; import Qustiontwo from "./qustiontwo"; import Qustionthree from "./qustionthree"; import Qustionfour from "./qustionfour"; // 按题目顺序维护组件列表,索引对应题目序号0-3 const questionList = [Qustionone, Qustiontwo, Qustionthree, Qustionfour]; function Questions() { // 当前展示的题目索引,默认显示第一题 const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); // 存储所有题目的答案,键为题目索引,值为选中的选项标识 const [answerMap, setAnswerMap] = useState({}); // 上一题切换逻辑 const goPrev = () => { if (currentQuestionIndex > 0) { setCurrentQuestionIndex(currentQuestionIndex - 1); } }; // 下一题切换逻辑 const goNext = () => { if (currentQuestionIndex < questionList.length - 1) { setCurrentQuestionIndex(currentQuestionIndex + 1); } }; // 更新对应题目的选中答案 const changeAnswer = (qIndex, optionValue) => { setAnswerMap(prev => ({ ...prev, [qIndex]: optionValue })); }; // 获取当前需要渲染的题目组件 const CurrentQuestionComp = questionList[currentQuestionIndex]; return ( <div> <h1>Question and Answer Program</h1> <CurrentQuestionComp selected={answerMap[currentQuestionIndex]} onSelect={(val) => changeAnswer(currentQuestionIndex, val)} /> {/* 第一题时禁用上一页按钮 */} <button onClick={goPrev} disabled={currentQuestionIndex === 0}>previous</button> {/* 最后一题时可根据需求替换为提交按钮,这里暂时禁用下一页 */} <button onClick={goNext} disabled={currentQuestionIndex === questionList.length - 1}>next</button> </div> ); } export default Questions;
第二步:改造题目子组件
以Qustionone.js为例,其余3个题目组件改造逻辑完全一致,仅需要替换题目内容和选项内容:
import React from "react"; function Qustionone({ selected, onSelect }) { // 维护当前题的选项列表,每个选项配置唯一值和展示文本 const options = [ { value: "q1-1", text: "Answer1" }, { value: "q1-2", text: "Answer2" }, { value: "q1-3", text: "Answer3" } ]; return ( <div> <p>Qustion 1 ?</p> {options.map(opt => ( <div key={opt.value} // 选项值和已选值匹配时,添加selected类名 className={selected === opt.value ? "selected" : ""} // 点击选项时触发父组件传入的回调,更新答案 onClick={() => onSelect(opt.value)} > {opt.text} </div> ))} </div> ); } export default Qustionone;
其余子组件修改注意事项
- 每个子组件都需要接收
selected和onSelect两个props,不需要自己维护内部选中状态 - 每个题的选项
value保证全局唯一即可,比如第二题选项值可设为q2-1/q2-2/q2-3,避免和其他题选项冲突 - 自行编写
.selected类对应的CSS样式,比如高亮背景、加粗边框等效果即可
实现后切换题目时,所有已选答案都存在父组件的answerMap状态中,不会因为子组件卸载丢失,切回历史题目时会自动匹配已选值添加选中样式,完全符合需求。
内容的提问来源于stack exchange,提问作者manoop m
相关产品推荐
相关产品推荐

