1

I am learning react and react forms and trying to create a dynamic and scalable form. I have set the state which has a type and based on the type, the respective input type shows up (so it could be a text, textarea, radio, select, date or checkbox). I am trying to write a function such that it will dynamically set the on change based on the type of the form input but i am stuck trying to achieve the same.

So if the type === select, event.target.value or type === checkbox, event.target.checked and so on.

Please check this working CodeSandbox.

Check out this Complete code snippet:-

import React from "react";
import "./styles.css";

class App extends React.Component {
  state = {
    Forms: [
      { name: "select", type: "select", options: ["a", "b", "c"] },
      { name: "Radio", type: "radio", options: ["a", "b", "c"] },
      { name: "Date", type: "date", value: "2018-07-22" },
      { name: "Text Input", type: "text", value: "text input" },
      {
        name: "Checkbox",
        type: "checkbox",
        options: ["a", "b", "c"],
        value: false
      },
      { name: "Textarea", type: "textarea", value: "text area" }
    ]
  };

  handleFormStateChange = (event, idx) => {
    const target = event.target;
    const form = [...this.state.forms];
    form[idx].value = target.type === "select" ? target.value : "";
    form[idx].value = target.type === "radio" ? target.value : "";
    form[idx].value =
      target.type === "date" ||
      target.type === "text" ||
      target.type === "textarea"
        ? target.value
        : "";
    form[idx].value = target.type === "checkbox" ? target.checked : "";
    this.setState({
      form
    });
  };

  getField = field => {
    switch (field.type) {
      case "date":
      case "text":
      case "textarea":
        return <input type={field.type} value={field.value} />;
      case "select":
        return (
          <select name={field}>
            {field.options.map(option => (
              <option key={option} value={option}>
                {option}
              </option>
            ))}
            ;
          </select>
        );
      case "radio":
      case "checkbox":
        return (
          <div>
            {field.options.map(option => (
              <label>
                {option}:
                <input
                  key={option}
                  type={field.type}
                  name={option}
                  value={option}
                />
              </label>
            ))}
          </div>
        );
      default:
        return <div>Unknown form field</div>;
    }
  };

  renderForm = () => {
    return this.state.Forms.map((field, index) => (
      <label key={index}>
        {field.name}
        {this.getField(field)}
      </label>
    ));
  };

  render() {
    return <div>{this.renderForm()}</div>;
  }
}

export default App;

I am still trying to learn and spend time with ReactJS so any help will be appreciated. Thank you so much. :)

Somethingwhatever
  • 1,390
  • 4
  • 32
  • 58

2 Answers2

1

This should work.

handleFormStateChange = (event, idx) => {
    const target = event.target;
    const form = [...this.state.Forms];
    form[idx].value = "";
    form[idx].value =
      target.type === "select-one" ? target.value : form[idx].value;
    form[idx].value = target.type === "radio" ? target.value : form[idx].value;
    form[idx].value =
      target.type === "date" ||
      target.type === "text" ||
      target.type === "textarea"
        ? target.value
        : form[idx].value;
    if (target.type === "checkbox") {
      if(!form[idx].selectedValues) {
        form[idx].selectedValues = {};    
      }
      form[idx].selectedValues[target.value] = target.checked;
    }
    this.setState({
      form
    });
  };

getField = (field, index) => {
    switch (field.type) {
      case "date":
      case "text":
      case "textarea":
        return (
          <input
            type={field.type}
            value={field.value}
            onChange={event => {
              this.handleFormStateChange(event, index);
            }}
          />
        );
      case "select":
        return (
          <select
            name={field}
            value={field.value}
            onChange={event => {
              this.handleFormStateChange(event, index);
            }}
          >
            {field.options.map(option => (
              <option key={option} value={option}>
                {option}
              </option>
            ))}
            ;
          </select>
        );
      case "radio":
      case "checkbox":
        return (
          <div>
            {field.options.map(option => (
              <label key={field.type + "op" + option}>
                {option}:
                <input
                  onChange={event => {
                    this.handleFormStateChange(event, index);
                  }}
                  key={option}
                  type={field.type}
                  name={option}
                  value={option}
                />
              </label>
            ))}
          </div>
        );
      default:
        return <div>Unknown form field</div>;
    }
  };

  renderForm = () => {
    return this.state.Forms.map((field, index) => (
      <label key={index}>
        {field.name}
        {this.getField(field, index)}
      </label>
    ));
  };

first of all, checkbox type can have multiple values selected, so we need to have a status for every option inside. So introduced selectedValues object on your form object.

Second thing to notice is, when a select element is defined, its event.target.type is select-one instead of select.

And the final thing we need to take care of is, re-use the original value of form[idx].value where your condition is falsified instead of using empty string. Using empty string for the else part would override your previously set value.

Maddy Blacklisted
  • 1,190
  • 1
  • 7
  • 17
0
  const handleChange= (e)=> {
    let v = e.target.type === "checkbox" ? e.target.checked : e.target.value;
      setState(
         {
           ...state,
           [e.target.name]: v
         }
      );
  };

Just check is it a checkbox or no. Another input types have a value property but checkbox has checked property. Don't forget to set a name prop for your input.

Vahid Alimohamadi
  • 4,900
  • 2
  • 22
  • 37