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

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;

改动说明

  1. 删掉了原来没用的ref和冗余的单个答案状态,所有选项统一用数组answerOptions管理,初始化就有3个空项符合需求
  2. 多选框的状态改存布尔值,修正了原来存字符串的错误逻辑,现在直接拿checked属性赋值
  3. 所有表单组件都走受控模式,值和状态绑定,不需要手动捞DOM值
  4. 提交的时候可以直接过滤掉空的选项再提交,避免无效数据

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.15 20:45:03