Handling React Forms with Mobx Observables

RisingStack's services:

Sign up to our newsletter!

In this article:

When you’re building a web application, you have to handle forms to get input from your users.

Unfortunately, forms in React are not that straightforward at the beginning; especially if you are used to a full featured-framework like Angular.js – and I’ve seen people asking about handling react forms with Mobx multiple times.

In this post, I am going to explain a simple approach to handle React forms, without using an already existing form library. At the same time, I’ll describe a few techniques and patterns that you can use in your applications.

This is the first part of the tutorial series about handling React forms using Mobx Observables.

  • First part: Handling the form data (you are reading it now)
  • Second part: Validating the form (coming soon)

Core ideas:

  • Handling inputs onChange events
  • Creating reusable components
  • How to use Higher-Order Components (HOC)
  • Simple data management and rendering with observables

I will start from the basic principle that lets us modify the form data and iterate over the idea until we reach a simple solution. Note that while I am going to use observables and Mobx, most of the code and ideas here can be applied in general.

Github repo available

There is a Github repo available with the full code created for this article.

I’ll indicate when to check it out (tags) in every section. It is highly recommended that you do so while reading this article because only a subset of the code will be displayed on this page.

If you’re already familiar with Mobx, I recommend to jump directly to the React forms section of the article.

What is mobx and why use it?

Mobx is a library that allows you to create observable data. It has bindings for React, which means that it allows React components to update automatically when the data they depend on changes.

It allowed me to greatly simplify my applications compared to the usually recommended flux architecture with libraries like Redux.

mobx data flow with react forms

Working with Mobx is simple because you can work with objects the way you have always done in javascript (simply changing object properties values) and you can also achieve great rendering performance with no effort at all.

immutables against observables, mobx against redux

So, if you don’t know Mobx yet, I encourage you to check their site and the presentations they have.

React forms

Let’s start with the handling of form inputs.

In React, there is a concept called “controlled input.” This means the following:

  • The input value is set with the props provided through React.
  • The form data and the input value is updated through an onChange handler.
// example inside a component
...
render () {
  return <input type="text"
      value={this.props.name}
      onChange={this.handleChange}/>
}

For further info, check out the React controlled forms documentation.

The onChange “trick”

Let’s start with a secret: the onChange handler.

It is about providing not only the new value but also “what” should be updated.

Given a certain form input, I will use the name attribute to tell what property needs to be updated along with its new value.

onChange (event) {
  this.props.onChange(event.target.name, event.target.value)
}

It is inspired by PHP, where it is possible to handle arrays in HTML forms like this:

<form action="person.php" method="POST">
  <input name="person[email]" />
  <input name="person[phone]" />
</form>

The form values would be parsed as you can imagine in PHP.

Result of $_POST:

array(
    'person' => array(
        'email' => ''
        'phone' => ''
    )
)

Back to javascript; imagine a person’s data (Name, address, job, …):

To update the name in javascript, the only thing you would need to do is:

person.fullName = 'Jack'

Let’s imagine a generic updateProperty function that should update any property of the person:

function updateProperty (key, value) {
  person[key] = value
}

Simple. Now let’s put the things together.

Creating the React form components

Article repo: git checkout step-1-basics

Let’s glue the pieces together with React and Mobx to handle a form for this person:

First, let’s create an observable person with mobx. This is done by passing your object to mobx.observable.

Then let’s create PersonForm.js: the React form component for a person, starting with the person’s name. It will receive the observable person data as a prop.

How does this work?

  1. When the user types in the field, the onChange handler gets the corresponding person property to update: “fullName”.
  2. It updates the person data by calling the updateProperty method with the new value.
  3. The field will be re-rendered by React with the updated value thanks to the component being a Mobx observer that is reacting to changes in the “observable person”.

Note: if you look at the repo code, I am creating the observable person data in the app constructor and pass it to the form.

It is up to you to decide how you provide the data to your form component and how you will submit it (fetch API, store, actions), but I’ll come back to it later. (App component gist)

First refactor: InputField component

Article repo: git checkout step-2-inputfield

So far, we have updated one property and, while we could simply do some copy paste to update the email and the job, we will do something better.

Let’s create an input component that will “emit” what we need by default, plus some extras.

  • My input is an observer.
  • By default, it will call the onChange handler with the field name and the new value.
  • Let’s add some extra markup: a label to show benefits of reusing components.

And that’s how I can use it in my person form:

  • I don’t need an onChange handler in my form component anymore.
  • I can pass the updateProperty handler directly to my inputs.

Important benefit of this approach

By default, React updates the whole component subtree and, as you might know, you can define the method shouldComponentUpdate to spare unnecessary updates. As a developer, you then either have to deal with immutables or do some tedious manual updates.

But, by using mobx-react observers, the shouldComponentUpdate method will be implemented for you. This means that updating one field will trigger the re-rendering of this field only. You get the best performance without any effort. React docs: shouldComponentUpdated

What about complex forms?

Actually, at this point, you already know how to deal with them. That’s the beauty of this simple approach.

Article repo: git checkout step-3-nestedform

Deep objects

My person had an address.

To update the address, consider it a nested form and apply the same principle.

Create a PersonAddress form component that is just the same as the “base” Person form component, and reuse the InputField component:

And use it in the Person form:

Arrays of objects

Article repo: git checkout step-4-form-array

Consider them arrays of forms.

For example our person now got some tasks:

Create a PersonTask form component. We can use the same concept for the address component.

Then, just “map”:

Second refactor: form capabilities as higher order component

Article repo: git checkout step-5-hoc

As you might have noticed, we are still repeating some code in every form / subform.

The form data update mechanism:


  constructor (props) {
    super(props)
    this.updateProperty = this.updateProperty.bind(this)
  }

  updateProperty (key, value) {
    this.props.address[key] = value
  }

Instead of this, let’s extract this logic to a higher order component.

What is a Higher Order Component (HOC)?

A higher order component is a function.

It takes a component as argument and will return another component that wraps it, adding any kind of behavior you want it to have.

In the case of our forms, we will create “asForm”; an HOC that provides the form data update mechanism to any component.

What you can see in the code:

  • asForm is a function.
  • Its first argument, MyComponent should be a component.
  • It returns a new component called Form that wraps MyComponent.
  • It adds the form update mechanism as a prop to MyComponent: updateProperty.
  • about the second argument formDataProp: it should be the name (string) of the prop that points to the form data. You might be passing more props to your form like UI related stuff for example. It’s a simple way to indicate what should be updated.

Using the asForm higher order component

Let’s take the address component and refactor it.

As you can see:

PersonAddress component now is very simple, we have extracted any logic related to the address updates.

  • We imported the asForm HOC and wrapped the address component, indicating which props has the form data. (last line)
  • We simply used the onChange handler provided by the asForm HOC, for the Inputs.

And that’s it. We can repeat the refactor process for the tasks forms (or any other). From now on, the developer only needs to care about the form presentation by providing the relevant inputs.

What about other types of input?

Article repo: git checkout step-6-radio-check

Choosing input types is about what you want from your user: you may want to force your users to choose only one option from many (radio), or as many optional options as they want (checkboxes).

You can apply to radio and checkboxes the same principle that was used for input [text|email|number]: emit name and value from the onChange.

While radio and checkboxes are “native” components of the browser, you can create your own input components / UX to achieve this. You can check the repo to see how radio and checkbox can be handled.(step-6-radio-check)

Last example: a list of checkboxes

Article repo: git checkout step-7-checklist

It was simple until now but, we don’t always have a simple value to update. What about arrays?

Let’s say that we want to ask a person what mascots she has. For this your model is an array of simple values like:
mascots: ['dog', 'cat'] and the list itself will present more animals.

We will follow the same principles like before:

  • First, let’s add a new handler to the asForm HOC. This handler will simply remove or add an element of an array. Let’s call it updateArray.
  • Create a component “InputCheckboxes” that takes a list of items and the list of currently selected items. It will render it as a list of checkboxes.

You can check the repo or this InputCheckboxes gist for implementation details.

It would be used in our PersonForm component as below.

const mascots = ['bird', 'cat', 'dog', 'iguana', 'pig', 'other']
//...
<InputCheckboxes items={mascots} name="mascots" checkedItems={person.mascots} onChange={updateArray}/>

As you can see, compared to previous examples, we are passing updateArray instead of updateProperty for the onChange handler.

Submitting the form

Article repo: git checkout step-8-submit

I have created a last step where your can check how to submit the form.

We simply have to pass a submit handler to the form component. This is where you might trigger an “action” and call your services API’s.

Conclusion

We have seen how easy it is to create reusable form helpers with a Higher Order Component. You can extend your form HOC update handlers to fit any of your data structure combined with any UX you wish with React components.

React views updates automatically and mobx optimizes the rendering.

Share this post

Twitter
Facebook
LinkedIn
Reddit