Categories
React What I Learned Today

React: Dynamic Form Building…Cool Stuff

I’ve built a few small projects. A lot of them were built so I make sure I actually understood what I was learning; taking what I learned and creating something unique. Most of the project I’ve built had some a text-box or a form.

Obviously, I would do this manually; as many do. However I came across an interesting ( and friggin cool! ) way of setting up your forms; dynamically, with React.

This will be a small post showing you how to build a simple form, dynamically. So lets get started.

Here is what we will be building, today:

A simple submission form.

I’m going to assume you already know how to setup your react environment, so I will not be explaining how to do that. If you’re new to react and need to know how to get your environment up and running, checkout this post.

Moving along…

This project only contains 2 components, FormBuilder and Input; Input being the child. Since FormBuilder will maintain state, I decided to set this up as a class-based-component; Input was left as presentation.

Here is what our components look like naked:

class FormBuilder extends React.Component {
	render (){
      return()
    }
};

const Input = props => {
  return ();
};

Let us first give our stateful component some state to maintain:

class FormBuilder extends React.Component {
  state = {
    reactForm: {
      firstname: {
        elementType: 'text',
        elementConfig: {
          placeholder: 'FirstName'
        },
        value: null,
      },
      lastname: {
        elementType: 'text',
        elementConfig: {
          placeholder: 'LastName'
        },
        value: null,
      }, 
      email: {
        elementType: 'email',
        elementConfig: {
          placeholder: 'Email'
        },
        value: null,
      },
      street: {
          elementType: 'text',
          elementConfig: {            
            placeholder: 'Street'
          },
          value: null
      },
      selection: {
        elementType: 'select',
        elementConfig: {
          options:['Option1', 'Option2','Option3','Option4']          
      }
    }
     }
 };
// the rest of the code omitted.

Ok, so what’s going on here? Well, in order for us to dynamically setup the form, we need a way to effectively execute just-that. The “reactForm” object houses all the other sub-objects; each one specifically setup to be associated with a particular type of input: firstname with the firstname-input-box, lastname with lastname-input-box, etc.

Note: I know I could have setup the state a little better ( a helper function would be great.), but I just wanted something quick an dirty so I write this post as quickly as possible.

Each sub-object is an object in itself which houses it’s own key value pairs. Lets quickly go over them:

elementType: The value for this property will represent the type attribute for a given element.

elementConfig: The value for this property will represent unique attributes for a given element, in this case it will only be placeholder and options.

value: This will house the input the user enters into the form.

Now, lets add a handler that will update state with the user’s input.

//above code omitted

updateInputHandler = (event,elementName) => {
    const updateReactForm = {...this.state.reactForm};
    const updatedElement = {...updateReactForm[elementName]};

    updatedElement.value = event.target.value;
    updateReactForm[elementName] = updatedElement;
    
    this.setState({reactForm: updateReactForm})    
  }

//below code omitted

Per the above, the handler will accept the event (value) from as the user enters in their information. We also have ‘elementName‘ argument that will be received. Why? Well, since we’re dynamically setting up these, we will need a way for react to know which state to update. Continue reading to find out how we’ll do this.

Remember our state? We will be using the keys as our elementNames by simply extracting those keys into a list and shipping them off to our child component with the following line of code:

  render() {
    // ['firstname', 'lastname', 'email'....]
    const elements = Object.keys(this.state.reactForm);

 //rest of code omitted.
 //Note the JavaScript code is implemented AFTER the 'render'
    //function and BEFORE the 'return' function.
 

Great! Now we have an array that we can map over in our return function:

render() {
  const elements = Object.keys(this.state.reactForm);
  return (
    <form className="FormBuilder">
      {
        elements.map((element) => {
          return (
            <Input 
              key={element}
              type={this.state.reactForm[element].elementType}
              pholder={this.state.reactForm[element].elementConfig['placeholder']}
              inputUpdated={(event)=> this.updateInputHandler(event, element)}
              options={this.state.reactForm.selection.elementConfig.options}/>
          )            
        })
      }
      <button>SUBMIT</button>
    </form>
  );
}

Let us go over the props being sent over to our child component, Input.

  • type={this.state.reactForm[element].elementType}

Our type prop will represent the attribute you see within an input element; take a look at exhibit-A:

<input type='text' />

  • pholder={this.state.reactForm[element].elementConfig[‘placeholder’]}

Our pholder prop will represent the attribute you see within an input element; take a look at exhibit-B:

<input type='text' placeholder='firstname'  />

  • inputUpdated={(event)=> this.updateInputHandler(event, element)}

The inputUpdated prop will represent the the handler( function) our child component will use to update state.


  • options={this.state.reactForm.selection.elementConfig.options}

The options prop represents the ‘option‘ element you see within a select tag:

<select for="blah">
  <option value="blah">Blah</option>
</select>

Lets head over to the Input component.

const input = props => {
    let formElement = null;

    switch (props.type) {
        case ('text' || 'number'):
            formElement = <input 
                            className={classes.Input}
                            placeholder={props.pholder} 
                            type={props.type}
                            onChange={props.inputUpdated} />;
            break;
        case ('select'):
            formElement = <select className={classes.Input}>
                                {
                                    props.options.map(option => {
                                        return (
                                            <option key={option} value={option}>
                                                {option}
                                            </option>
                                        )
                                    })
                                }
                            </select>;
            break;
        default:
            formElement = <input 
                            className={classes.Input}
                            placeholder={props.pholder} 
                            type={props.type}
                            onChange={props.inputUpdated} /> 
                            
    }
    return (
        <div>
            {formElement}
        </div>
        
    )
}

Per the code above, to get things working, we’re using JavaScript’s switch statement. The element rendered will depend on the information received via props.

You should also notice the ‘mapping’ we’re doing to render all the options for the select element.

Hope this helps.