ReactJS 动态创建表单字段组件及提交时存储字段数据的实现问题
实现方案
核心思路是用数组类型的state统一管理所有答案选项,走受控组件模式,不需要额外用ref捞值,逻辑更清晰。
第一步:修改 PollCreate.js
//Main component for poll creation import React, { useState } from "react"; import PollCreateAnswerOption from "./PollCreateAnswerOption"; const PollCreate = (props) => { const [enteredTitle, setEnteredTitle] = useState(""); const [enteredDescription, setEnteredDescription] = useState(""); // 用数组存所有答案选项,默认初始化3个空选项 const [answerOptions, setAnswerOptions] = useState([ { id: Date.now() + 1, text: "" }, { id: Date.now() + 2, text: "" }, { id: Date.now() + 3, text: "" } ]); const [selectedPrivate, setSelectedPrivate] = useState(false); const [selectedMultiple, setSelectedMultiple] = useState(false); const [selectedLogin, setSelectedLogin] = useState(false); const [selectedIpcheck, setSelectedIpcheck] = useState(false); // 答案选项值变更回调 const handleAnswerChange = (id, newText) => { setAnswerOptions(prev => prev.map(item => { if (item.id === id) return { ...item, text: newText }; return item; })); }; // 删除答案选项回调 const handleDeleteAnswer = (id) => { // 若要最少保留2个选项可加判断:if(answerOptions.length <=2) return setAnswerOptions(prev => prev.filter(item => item.id !== id)); }; // 新增答案选项 const addAnswerOptionHandler = () => { setAnswerOptions(prev => [...prev, { id: Date.now(), text: "" }]); }; const privateSelectedHandler = (event) => { setSelectedPrivate(event.target.checked); }; const multipleSelectedHandler = (event) => { setSelectedMultiple(event.target.checked); }; const loginSelectedHandler = (event) => { setSelectedLogin(event.target.checked); }; const ipcheckSelectedHandler = (event) => { setSelectedIpcheck(event.target.checked); }; const onSubmitHandler = (event) => { event.preventDefault(); // 过滤掉空的答案选项再提交(可选) const validAnswerOptions = answerOptions.filter(item => item.text.trim() !== ""); const newPollData = { title: enteredTitle, description: enteredDescription, answerOptions: validAnswerOptions, options: { private: selectedPrivate, multiple: selectedMultiple, login: selectedLogin, ipcheck: selectedIpcheck, }, }; props.onSaveNewPoll(newPollData); }; return ( <React.Fragment> <form onSubmit={onSubmitHandler}> <h2 className="mb-4 text-center">Create a Poll</h2> <div className="text-left"> <div className="grid grid-cols-1 mb-4"> <h4 className="mb-2">Title</h4> <input type="text" placeholder="Ask your question..." value={enteredTitle} onChange={(e) => setEnteredTitle(e.target.value)} /> </div> </div> <div className="text-left"> <div className="grid grid-cols-1 mb-4"> <h4 className="mb-2">Description (optional)</h4> <input type="text" placeholder="Describe the poll..." value={enteredDescription} onChange={(e) => setEnteredDescription(e.target.value)} /> </div> </div> <div className="text-left"> <div className="grid grid-cols-1 mb-2 formtext"> <h4 className="mb-2">Answer Options</h4> {/* 遍历数组渲染所有答案选项组件,删掉原来硬编码的两个input */} {answerOptions.map(option => ( <PollCreateAnswerOption key={option.id} optionId={option.id} value={option.text} onAnswerChange={handleAnswerChange} onDelete={handleDeleteAnswer} /> ))} </div> </div> <button className="btn-primary mb-2" type="button" onClick={addAnswerOptionHandler} > Add Answer </button> <div className="text-left"> <div className="grid grid-cols-1 mb-6"> <h4 className="mb-2">Options</h4> <div className="flex items-center"> <input type="checkbox" className="mx-3" checked={selectedPrivate} onChange={privateSelectedHandler} /> <p>Private (only via direct link)</p> </div> <div className="flex items-center"> <input type="checkbox" className="mx-3" checked={selectedMultiple} onChange={multipleSelectedHandler} /> <p>Allow multiple choices</p> </div> <div className="flex items-center"> <input type="checkbox" className="mx-3" checked={selectedLogin} onChange={loginSelectedHandler} /> <p>Voters must log in to vote</p> </div> <div className="flex items-center"> <input type="checkbox" className="mx-3" checked={selectedIpcheck} onChange={ipcheckSelectedHandler} /> <p>Check for duplicate IP</p> </div> </div> </div> <button type="submit" className="btn-primary mx-2"> Create Poll </button> <button type="button" className="btn-alt-onwhite mx-2"> Advanced Settings </button> </form> </React.Fragment> ); }; export default PollCreate;
第二步:修改 PollCreateAnswerOption.js
不需要forwardRef了,直接走受控组件传值即可:
import React from "react"; const PollCreateAnswerOption = (props) => { const answerOptionChangeHandler = (event) => { // 把当前选项id和新值传给父组件 props.onAnswerChange(props.optionId, event.target.value); }; const deleteHandler = () => { props.onDelete(props.optionId); }; return ( <div className="flex"> <input type="text" className="mb-3 formtext w-full" placeholder="Add an answer..." value={props.value} onChange={answerOptionChangeHandler} /> <button className="btn-alt-onwhite p-1 h-9" type="button" onClick={deleteHandler} > X </button> </div> ); }; export default PollCreateAnswerOption;
改动说明
- 删掉了原来没用的ref和冗余的单个答案状态,所有选项统一用数组
answerOptions管理,初始化就有3个空项符合需求 - 多选框的状态改存布尔值,修正了原来存字符串的错误逻辑,现在直接拿
checked属性赋值 - 所有表单组件都走受控模式,值和状态绑定,不需要手动捞DOM值
- 提交的时候可以直接过滤掉空的选项再提交,避免无效数据
内容的提问来源于stack exchange,提问作者Alechilles
相关产品推荐
相关产品推荐

