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

React问卷应用移除问题后输入值错位问题的解决方法

问题原因
  • 错误将组件实例存入state:你在drafts数组里直接存储<MultipleChoiceDraft />组件实例,这违反了React状态管理的核心原则——state应该存储数据而非UI组件。React无法正确追踪这些预创建组件的生命周期,删除操作后组件实例未被正确卸载,导致旧输入内容残留。
  • 使用index作为key:在drafts.map中用循环index作为列表项的key,删除中间元素后,后续元素的index会发生变化,React的Diff算法会复用原有组件而非重新渲染,导致旧状态被保留在错误位置。
修复方案

1. 重构状态存储:存数据而非组件实例

修改QuestionsDraft的drafts状态,只存储每个问题的纯数据结构(id、问题文本、选项、答案等),不再存储组件实例。

2. 让子组件成为受控组件

将MultipleChoiceDraft的内部状态(question、choices、answer)提升到父组件统一管理,通过props传递数据和更新回调,确保每个问题的状态与对应数据绑定。

3. 使用唯一id作为key

用每个问题的唯一id作为列表项的key,而非循环index,确保React能准确识别每个组件实例。

完整修复代码

QuestionsDraft.js

const QuestionsDraft = () => {
    // 存储问题数据,而非组件实例
    const [drafts, setDrafts] = useState([
        { 
            id: 0, 
            question: "", 
            answer: null, 
            choices: {0: "", 1: "", 2: "", 3: ""} 
        },
        { 
            id: 1, 
            question: "", 
            answer: null, 
            choices: {0: "", 1: "", 2: "", 3: ""} 
        }
    ])

    const remove = (questionId) => {
        setDrafts(prev => prev.filter(draft => draft.id !== questionId))
    }

    // 更新问题文本的回调
    const updateQuestion = (questionId, newText) => {
        setDrafts(prev => prev.map(draft => 
            draft.id === questionId 
                ? {...draft, question: newText} 
                : draft
        ))
    }

    // 更新选项的回调
    const updateChoice = (questionId, choiceIndex, newText) => {
        setDrafts(prev => prev.map(draft => {
            if (draft.id === questionId) {
                const newChoices = {...draft.choices}
                newChoices[choiceIndex] = newText
                return {...draft, choices: newChoices}
            }
            return draft
        }))
    }

    // 设置答案的回调
    const setAnswer = (questionId, answerIndex) => {
        setDrafts(prev => prev.map(draft => 
            draft.id === questionId 
                ? {...draft, answer: answerIndex} 
                : draft
        ))
    }

    return (
        <section>
            {/* Other Codes */}
            <ul className="flex flex-col gap-8">
                {drafts.map((draft) => (
                    // 使用唯一id作为key
                    <li key={draft.id}>
                        {/* 渲染组件时传递数据和回调 */}
                        <MultipleChoiceDraft 
                            question={draft.question}
                            answer={draft.answer}
                            choices={draft.choices}
                            onUpdateQuestion={(text) => updateQuestion(draft.id, text)}
                            onUpdateChoice={(index, text) => updateChoice(draft.id, index, text)}
                            onSetAnswer={(index) => setAnswer(draft.id, index)}
                        />
                        <button onClick={() => remove(draft.id)}>Remove Question</button>
                    </li>
                ))}
            </ul>
        </section>
    )
}

MultipleChoiceDraft.js

// 受控组件,接收props中的数据和回调
const MultipleChoiceDraft = ({
    question,
    answer,
    choices,
    onUpdateQuestion,
    onUpdateChoice,
    onSetAnswer
}) => {
    return (
        <form>
            <input 
                placeholder="Question" 
                value={question}
                onChange={(e) => onUpdateQuestion(e.target.value)} 
            />
            <ul>
                {Object.values(choices).map((choice, index) => (
                    <li key={index}>
                        <input
                            value={choice}
                            type="text"
                            onChange={(e) => onUpdateChoice(index, e.target.value)}
                        />
                       <button 
                            type="button"
                            onClick={(e) => {
                                e.preventDefault(); // 阻止表单默认提交
                                onSetAnswer(index)
                            }}
                        >
                            Set as Answer
                        </button>
                    </li>
                ))}
            </ul>
        </form>
    )
}
关键修复点解释
  • 状态存储优化:drafts现在存储纯数据,React可通过数据变化正确触发组件的渲染与卸载,删除问题时对应数据被移除,关联的MultipleChoiceDraft组件也会被正确卸载,输入内容不会残留。
  • 受控组件设计:子组件不再维护内部状态,所有数据由父组件统一管理,确保数据与UI完全同步。
  • 唯一key使用:用问题的id作为key,即使数组元素顺序变化,React也能准确识别每个组件,避免复用错误的组件实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 05:26:17