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. :)

1

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

Comments

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.

1 Comment

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.