简体   繁体   中英

Building dynamic form with dropdown and radio button

I'm trying to build a dynamic form with react which is auto populated with components when received data from api, here I'm able to display and render the components which are the n numbers of dropdowns and radio buttons on my form basis on what I'll be receiving from the api response, but still I want to pass the data selected from the from on onSubmit() function. I'm facing certain challenges in collecting the data altogether, as I'm using rfce, I can get data of single selection whereas I want a collective selection data. Below attached is my code.

This is the main card component. Which is responsible for getting api and passing props to sub components that are dropdowns and radio button based on their types

import React, { useEffect, useState } from "react";
import { Card, Button, FloatingLabel, Form } from "react-bootstrap";
import "bootstrap/dist/css/bootstrap.min.css";
import {
  SurveyDropdown,
  SurveyRadioButton,
} from "./subcomponents/SurveyComponents";
import { getQuestions } from "../util/http";

const SurveyCard = () => {
  const [surveyData, setSurveyData] = useState([]);
  useEffect(() => {
    // Update the document title using the browser API
    getQuestions()
      .then((resp) => {
        setSurveyData(resp.data);
      })
      .catch((err) => {
        console.log(err);
        if (
          !err.response ||
          !err.response.data ||
          !err.response.data.errorCode
        ) {
          console.log("server failed to response");
          return;
        }
        switch (err.response.data.errorCode) {
          case 50: // Survey submitted
            window.location.replace("/surveysubmitted");

            break;

          default:
            console.error("Error code not registered ");
            break;
        }
      });
  }, []);
  return (
    <>
      <Card>
        <Card.Header></Card.Header>
        <Card.Body>
          <Card.Title>Please provide your feedback</Card.Title>
          {surveyData.map((question) => {
            if (question.type === "dropdown") {
              return <SurveyDropdown key={question.id} value={question} />;
            } else if (question.type === "button") {
              return <SurveyRadioButton value={question} />;
            }
          })}
          <FloatingLabel
            controlId="floatingTextarea2"
            label="Comments"
            className="for-spacing"
          >
            <Form.Control
              as="textarea"
              placeholder="Leave a comment here"
              style={{ height: "100px" }}
            />
          </FloatingLabel>{" "}
          <br />
          <Button variant="primary">Submit</Button>
        </Card.Body>
      </Card>
      <br />
      <br />
    </>
  );
};

export default SurveyCard;

This is component which is rendering all the dropdowns and radio buttons. Here I'm trying to get the collective data from all displayed dropdowns and radio button, but instead am getting only one state a time.

import React, { useState } from "react";
import { Form, FloatingLabel } from "react-bootstrap";

export const SurveyDropdown = (props) => {
  const [selectedOption, setSelectedOption] = useState();
  console.log("Selected Option", selectedOption);
  function handleSelectChange(event) {
    setSelectedOption(event.target.value);
  }

  return (
    <React.Fragment>
      <FloatingLabel
        className="for-spacing"
        controlId="floatingSelect"
        label={props.value.surveyQuestion}
      >
        <Form.Select onChange={handleSelectChange}>
          {props.value.option.map((surveyD, index) => (
            <option
              value={surveyD}
              key={index}
              selected={surveyD === selectedOption}
            >
              {surveyD}
            </option>
          ))}
        </Form.Select>
      </FloatingLabel>
    </React.Fragment>
  );
};

export const SurveyRadioButton = (props) => {
  const [checkedOption, setCheckedOption] = useState([]);
  console.log("Checked Option", checkedOption);
  function handleCheckedChange(event) {
    setCheckedOption(event.target.value);
  }

  return (
    <div className="for-spacing">
      <label>{props.value.surveyQuestion}</label>
      <Form>
        {["radio"].map((type) => (
          <div key={`inline-${type}`} className="mb-3">
            {props.value.option.map((answer, index) => {
              return (
                <Form.Check
                  inline
                  label={answer}
                  key={index}
                  name="group1"
                  type="radio"
                  value={answer}
                  id={`inline-${type}-1`}
                  onChange={handleCheckedChange}
                />
              );
            })}
          </div>
        ))}
      </Form>
    </div>
  );
};

what shall I try, please help! Thanks!

I have so many remarks, but let's try to focus:

If you want to have controlled inputs you need to define the whole state in the upper component (=SurveyCard) and then pass down a callback for setting state accordingly.

If you want to have uncontrolled inputs first of all you need to have one single Form, not many. So remove the Form from Dropdown and Button components and wrap your SurveyCard Card.Body with it. On submit, you need to define a strategy on how to get those values out. You can for example use FormData object and iterate over the fields or - my preferable option - use some library for uncontrolled forms (ie "react-hook-form).

Please see into this sandbox with a working example: https://codesandbox.io/s/summer-grass-3gn4gm . I have tried to mock your data.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM