All Articles

32 min read • By Kyle Truong • Published 24 Sep 2017

Forms with Redux Form v7

A Bit of Background

There are few things less appealing than forms but they’re still one of the most direct ways for a user to dump a hunk of data into your server. To make the process of filling a form as painless as possible, one needs a good form system that handles common concerns like:

Handling field and form data Validation Submission Integration with the rest of our application

There are many form systems in React. The most common choices seem to be either going with vanilla React components and state or using one of the many libraries that expose Higher Order Components (HOCS) that may or may not integrate with Redux.

‘Redux Form’ is one of those libraries, one that manages your form state in Redux. I haven’t tried all other options so I’m not claiming this is the one true form tool, but I’ve tried vanilla React forms and custom in-house React form systems, and ‘Redux Form’ has provided the nicest form-making experience of them all.

What We’re Doing

We’re going to make a form.

In loose order, we’ll be covering these topics using Redux Form v7:

  • Hooking up Redux Form to our Redux store
  • Splitting up data and presentational concerns into containers and components
  • Making custom and reusable form components (text inputs, dropdowns, datepickers, radios, and checkboxes)
  • Styling with Tachyons
  • Using built in action creators and selectors to make dynamic fields that depend on the values of other fields
  • Real-time client side validation (both, upon submission and per keystroke)
  • Basic form submission

Requirements

  • Node v6+
  • npm/yarn
  • Create-react-app
// package.json

{
  "name": "redux-form-v7-example",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "moment": "^2.18.1",
    "react": "^15.6.1",
    "react-addons-shallow-compare": "^15.6.0",
    "react-dates": "^12.6.0",
    "react-dom": "^15.6.1",
    "react-redux": "^5.0.6",
    "react-scripts": "1.0.13",
    "redux": "^3.7.2",
    "redux-form": "^7.0.4",
    "reselect": "^3.0.1",
    "tachyons": "^4.8.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}

Setting Up Our Store

Use create-react-app to create a new react application and then structure the folders and files to follow this shape:

File tree of our current redux-form project

Our entry point will be index.js. This is where we wrap our main component, FormContainer, with our Redux store, and render our app onto the page using ReactDOM.

// Index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';

import FormContainer from './modules/form/form.container';
import configureStore from './store';

const store = configureStore();

ReactDOM.render(
  <Provider store={store}>
    <FormContainer />
  </Provider>,
  document.getElementById('root')
);

The store.js file exports a function that configures a store. It’s set up in such a way that we can easily plug in middleware and enhancers like Redux Devtools.

// store.js

import { createStore, applyMiddleware, compose } from 'redux';
import createReducer from './reducers';

const composeEnhancers =
  typeof window === 'object' &&
  (window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
    ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
    : compose);

export function configureStore() {
  const middlewares = [];
  const store = createStore(
    createReducer(),
    {},
    composeEnhancers(applyMiddleware(...middlewares))
  );
  return store;
}

export default configureStore;

And the corresponding reducer.js file exports a function that combines all reducers into one. I like to keep my root reducer file separate from my store to keep things more modular.

// reducers.js

import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';

export default function createReducer(extraReducerObjects = {}) {
  return combineReducers({
    form: formReducer,
    ...extraReducerObjects
  });
}

For the form.container.js and form.component.js files let’s just make filler components for now, like this:

src/modules/form/form.container.js

import React, { Component } from 'react';

class FormContainer extends Component {
  render() {
    return (
      <div>
        a what whAT
      </div>
    );
  }
}

export default FormContainer;

Splitting Up Data and Presentational Concerns

It’s common practice in React applications to separate concerns of data and presentation into containers and components. We’re going to put all of the stuff that deals with data and state in the container, and all of the stuff that deals with how the form looks into the component. This will serve as a loose guide as to how we structure our components.

Separating our components in this way not only makes it easier to test but also easier to reason. If you want to change how something looks you change the component, and If you want to change how something works you change the container.

Wrapping Our Form Container with Redux Form’s Higher Order Component

Let’s start making our container, a normal React component that renders FormComponent and wraps it with Redux Form’s reduxForm() helper. reduxForm() is a function that takes in a form configuration object and returns a HOC, a function that takes and returns a component.

The purpose of such wrapping in this case is to return the wrapped component with a bunch of helpers — functions that can change the state of the form or give you information as to whether a form field was touched or validated, or which fields are registered — passed through as props.

// src/modules/form/form.container.js

import React from 'react';
import { reduxForm } from 'redux-form';

import FormComponent from './form.component';

export const FormContainer = props => {
  return (
    <FormComponent />
  );
}

const formConfiguration = {
  form: 'my-very-own-form'
}

export default reduxForm(formConfiguration)(FormContainer);

If you place a console.log(props); right before the return in the container you can actually see these props for yourself:

Console-logged contents of redux-form props

All of the above helpers act sort of like a master control for the form you wish to wrap by providing action creators to change the state of the form. Here’s a comprehensive list from the documentation:

https://redux-form.com/7.0.4/docs/api/props.md/

Let’s use one of these props, handleSubmit(), a function that runs validation and invokes a function we define and pass in with all of the form values passed to it as a parameter.

We’ll pull out handleSubmit from the passed props with ES6 destructuring and define our own submit handler to be used with handleSubmit, and then pass the two props down to our form component:

// src/modules/form/form.container.js:

import React from 'react';
import { reduxForm } from 'redux-form';

import FormComponent from './form.component';

export const FormContainer = ({ handleSubmit }) => {
  const submitForm = (formValues) => {
    console.log('submitting Form: ', formValues);
  }

  return (
    <FormComponent
      onSubmit={submitForm}
      handleSubmit={handleSubmit}
    />
  );
}

const formConfiguration = {
  form: 'my-very-own-form'
}

export default reduxForm(formConfiguration)(FormContainer);

Redux Form’s Field Component

Besides the reduxForm HOC provided by Redux Form, another big helper is the Field component. It’s a normal component that represents a field in a form, but what makes it really unique is that each Field is individually connected to Redux state.

If you’ve ever created forms with React then you should be familiar with the concept of controlled components.

https://facebook.github.io/react/docs/forms.html

HTML5 form tags like ‘input’ and ‘textarea’ often maintain their own state that gets updated upon user input. This leads to unnecessary complications when also handling React and Redux state because now data state has to be synced on three different levels, with no huge benefit. It’s much easier to have the user input update a single source of truth (Redux) and have the inputs draw their state and value from that source. It makes for a more unidirectional flow that’s easier to follow and debug.

We can use Field as is like this (a name prop is required to set the key in the reducer):

// src/modules/form/form.component.js

import React from 'react';
import { Field } from 'redux-form';

export const FormComponent = ({ handleSubmit, onSubmit }) => {
  return (
    <div>
      <h1>My Very own Form</h1>
      <form onSubmit={handleSubmit(onSubmit)}>
        <Field
          name="firstName"
          component="input"
        />
        <button type="submit">Submit</button>
      </form>
    </div>
  );
}

export default FormComponent;

Another neat feature of Field is that we can pass our own component into it via the component prop because Field is essentially a component that acts much like a HOC in that in can accept and return a new component. And similar to how reduxForm wrapped our container and passed in a lot of utility functions via props, Field does the same and passes a lot of utility helpers onto the component we pass in via the component prop.

By default we can pass in strings like ‘input’ that will render an input tag for us, but if we want more customization as to how our field will look or how it manages data and handles events, we need to pass in our own custom component.

Here’s how our form looks right now:

Example of redux-form rendered in browser

And look at all the cool data we got in Redux State! Each form and field is connected to the Redux Form reducer and provide information we can use in our own components.

a Redux DevTools chart of our Redux store split into its reducers (a Redux DevTools chart of our Redux store split into its reducers)

And when we click the submit button, it’ll invoke our submitForm method with the form data as we discussed:

Console-logged results of submitting a form with redux-form

Making a Custom Text Input Component

Let’s create a text.js file in our new folder, components, which will return a component that receives props from Redux Form’s Field component and renders an input tag.

// src/modules/components/text.js

import React from 'react';

export const Text = ({ label, input }) => {
  console.log('inputStuff: ', input);
  return (
    <div>
      <div>
        {label}
      </div>
      <div>
        <input {...input} placeholder={label} type="text" />
      </div>
    </div>
  );
}

export default Text;

Notice how we spread props.input into our input field. The props passed by Field include a bunch of event handlers to handle clicks, changes, blurs, and other standard form field events, by accepting an event object and dispatching an action to change the state of the form in the form’s reducer.

Redux-form input props

Of course, each of these methods can be further customized if you want to implement your own onClick, onChange, or whichever, maybe to hit an API endpoint before dispatching an action to Redux or filtering through some data and performing other side effects. For now, we’ll keep it simple and stick with the default ones provided.

To use our new component, we just import it and feed it as a component prop on our Field:

// form/form.component.js:

import React from 'react';
import { Field } from 'redux-form';
import Text from '../components/text';

export const FormComponent = ({ handleSubmit, onSubmit }) => {
  return (
    <div>
      <h1>My Very own Form</h1>
      <form onSubmit={handleSubmit(onSubmit)}>
        <Field
          name="firstName"
          label="First Name"
          component={Text}
        />
        <button type="submit">Submit</button>
      </form>
    </div>
  );
}

export default FormComponent;

Styling with Tachyons

As a quick aside, our forms look ugly right now. Yes, there is normal css, sass, postcss, styled-components, and a plethora of other CSS tools we can use, but we’ll be using Tachyons for now. It’s simple, expressive, responsive, and composable. I like to think of it like bootstrap but much lighter, but It’s easier to show than explain.

First we need to import tachyons into our root component:

// Index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import "tachyons"

import FormContainer from './modules/form/form.container';
import configureStore from './store';

const store = configureStore();

ReactDOM.render(
  <Provider store={store}>
    <FormContainer />
  </Provider>,
  document.getElementById('root')
);

Then we start styling. ‘m’ stands for margin, ‘w’ for width, ‘p’ for padding, ‘v’ for vertical (top and bottom), ‘a’ for all (top, bottom, left, right), and the numbers are general sizes. Tachyons class names were made to be short but to resemble native CSS properties as close as possible.

// src/modules/component/text.js

import React from 'react';

export const Text = ({ label, input }) => {
  return (
    <div className="mv4 w-100">
      <div className="b sans-serif pv2 w-100">
        {label}
      </div>
      <input
        {...input}
        placeholder={label}
        type="text"
        className="pa2 ba b--black-40 w-100"
      />
    </div>
  );
}

export default Text;
// src/modules/form/form.component.js

import React from 'react';
import { Field } from 'redux-form';
import Text from '../components/text';

export const FormComponent = ({ handleSubmit, onSubmit }) => {
  return (
    <div className="flex flex-column justify-center items-center">
      <h1>My Very own Form</h1>
      <form
        className="w-80"
        onSubmit={handleSubmit(onSubmit)}
      >
        <Field
          name="firstName"
          label="First Named"
          component={Text}
        />
        <button
          type="submit"
          className="link br2 bg-blue white dim pa3 f6 sans-serif b--blue ba"
        >
          Submit
        </button>
      </form>
    </div>
  );
}

export default FormComponent;

And now our form looks slightly better:

simple styled Redux-form example

Let’s just add a few more fields so that we have more to play with. We can reuse the text component we just made like this:

// form/form.component.js:

import React from 'react';
import { Field } from 'redux-form';
import Text from '../components/text';

export const FormComponent = ({ handleSubmit, onSubmit }) => {
  return (
    <div className="flex flex-column justify-center items-center">
      <h1>My Very own Form</h1>
      <form
        className="w-80"
        onSubmit={handleSubmit(onSubmit)}
      >
        <Field
          name="firstName"
          label="First Named"
          component={Text}
        />
        <Field
          name="lastName"
          label="Last Name"
          component={Text}
        />
        <Field
          name="email"
          label="Email"
          component={Text}
        />
        <button
          type="submit"
          className="link br2 bg-blue white dim pa3 f6 sans-serif b--blue ba"
        >
          Submit
        </button>
      </form>
    </div>
  );
}

export default FormComponent;

And they’ll all be tracked in Redux State:

simple styled Redux-form example 2

Making a Custom Select (or Dropdown)

Sometimes it just makes sense to select an option from a dropdown rather than manually typing in an answer. We use the default HTML5 select tag along with option to structure our dropdown, but we still keep the state of the field in our reducer by passing in the props passed by Field. Here’s how you would make a basic, reusable select component:

// src/modules/components/select.js

import React from 'react';

export const Select = props => {
  const renderSelectOptions = (key, index) => {
    return (
      <option
        key={`${index}-${key}`}
        value={key}
      >
        {props.options[key]}
      </option>
    );
  }

  if (props && props.options) {
    return (
      <div className="mv3 w-100">
        <div className="b sans-serif pv2 w-100">{props.label}</div>
        <select {...props.input} className="pa2 input-reset ba b--black-40 w-100">
          <option value="">Select</option>
          {Object.keys(props.options).map(renderSelectOptions)}
        </select>
      </div>
    )
  }
  return <div></div>
}

export default Select;

Now we can add another Field to our form that takes in our select component and a bunch of options we pass through as props:

import React from 'react';
import { Field } from 'redux-form';

import Text from '../components/text';
import Select from '../components/select';

export const FormComponent = ({ handleSubmit, onSubmit }) => {
  return (
    <div className="flex flex-column justify-center items-center">
      <h1>My Very own Form</h1>
      <form
        className="w-80"
        onSubmit={handleSubmit(onSubmit)}
      >
       ...
        <Field
          name="meatChoice"
          label="Meat Choice"
          component={Select}
          options={{
            pork: 'Pork',
            beef: 'Beef',
            chicken: 'Chicken'
          }}
        />

       ...

export default FormComponent;

Making a Custom Radio Input

HTML5 radio inputs are a bit weird. There’s no explicit ‘radio’ tag like there is for ‘select’, so it’s the same as our ‘input’ tag only with a specified type of ‘radio’.

This is how it looks like in normal HTML5:

<form>
  <input type="radio" name="gender" value="male" checked> Male<br>
  <input type="radio" name="gender" value="female"> Female<br>
  <input type="radio" name="gender" value="other"> Other
</form>

So how do we convert that to React/Redux Form? Maybe we could use three separate Field components and make them all of type radio, but that leads to a lot of boilerplate just to render one field. Ideally, we want something like our ‘text’ input or ‘select’ input where we feed a single component into the component prop of Field.

We’ll use the same pattern as we did in our select component, where we passed in an ‘options’ prop that we mapped over to render individual snippets of JSX.

Keep in mind that for radio inputs, each input option has to have the same name prop while still maintaining a unique key to satisfy React:

// src/modules/components/radio.js

import * as React from 'react';
import { Field } from 'redux-form';

export const Radio = props => {
  if (props && props.input && props.options) {
    const renderRadioButtons = (key, index) => {
      return (
        <label className="sans-serif w-100" key={`${index}`} htmlFor={`${props.input.name}-${index}`}>
          <Field
            id={`${props.input.name}`}
            component="input"
            name={props.input.name}
            type="radio"
            value={key}
            className="mh2"
          />
          {props.options[key]}
        </label>
      )
    };
    return (
      <div className="mv3 w-100">
        <div className="b sans-serif pv2 w-100">
          {props.label}
        </div>
        <div>
          {props.options &&
            Object.keys(props.options).map(renderRadioButtons)}
        </div>
      </div>
    );
  }
  return <div></div>
}

export default Radio;

And now we can plop it into our form just like this:

// src/modules/form/form.component.js:

<Field
  name="spiceLevel"
  label="Spice Level"
  component={Radio}
  options={{
    mild: 'Mild',
    medium: 'Medium',
    hot: 'hot'
  }}
/>

Redux-form radio input example

Making a Custom Checkbox

Little boxes that can be checked or unchecked. It’s worth mentioning that even though a checkbox appears to have two states, checked and unchecked, behind the scenes, it actually has three states, checked (true), unchecked (false), and unchecked (undefined). Keep this in mind as it may bite you later on.

// src/modules/components/checkbox:

import React from 'react';

export const Checkbox = props => {
  return (
    <div className="flex items-center mv4 w-100">
      <input
        {...props.input}
        className="mr2"
        type="checkbox"
        checked={props.input.value}
      />
      <div className="sans-serif">{props.label}</div>
    </div>
  );
}

export default Checkbox;
// src/modules/form/form.component.js

...
<Field
  name="wantsFries"
  label="Would you like fries with that?"
  component={Checkbox}
/>
...

Integrating a Datepicker Component

There are plenty of libraries offering datepickers, many of which expose a single component we can drop in. The tricky part is that these datepicker components often have their own React state and spreading our Field props into these components often ends up not working.

It’s okay for the datepicker to have its own internal state to keep track of things like whether to display the UI or not, but we still want to keep the value of the field tracked in Redux state, like the rest of our fields.

Redux Form comes with many built in action creators, one of which can explicitly change the value of a Field. We pass that action creator, change, down through our component as props and use it in combination with a custom handleDateChange event handler to dispatch an action to change the value of the field in Redux every time the date changes.

// src/modules/components/datepicker.js

import React from 'react';
import moment from 'moment';
import { SingleDatePicker } from 'react-dates';
import 'react-dates/lib/css/_datepicker.css';

export class Datepicker extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      date: moment(),
      focused: false
    };
  }

  changeActiveDateWidget = () => {
    this.setState({
      activeDateWidget: !this.state.activeDateWidget,
    });
  }

  handleDateChange = (date) => {
    this.setState({ date });
    this.props.change(this.props.input.name, date)
  }

  render() {
    return (
      <div className="mv4 w-100">
        <div className="b sans-serif pv2 w-100">{this.props.label}</div>
        <SingleDatePicker
          date={this.state.date} // momentPropTypes.momentObj or null
          onDateChange={this.handleDateChange} // PropTypes.func.isRequired
          focused={this.state.focused} // PropTypes.bool
          onFocusChange={({ focused }) => this.setState({ focused })} // PropTypes.func.isRequired
          showClearDate={true}
          numberOfMonths={1}
        />
      </div>
    );
  }
}

export default Datepicker;
// src/modules/form/form.component.js

...
import Datepicker from '../components/datepicker';

export const FormComponent = ({ handleSubmit, onSubmit, formValues, change }) => {
  return (
    <div className="flex flex-column justify-center items-center">
      <h1>My Very own Form</h1>
      <form
        className="w-80"
        onSubmit={handleSubmit(onSubmit)}
      >
        ...
        <Field
          name="orderDate"
          label="Order Date"
          component={Datepicker}
          change={change}
        />
...

export default FormComponent;

Redux-form datepicker input example

Redux-form datepicker input redux store example

Form Selectors and Field-dependent Values

Let’s say you needed to create a dynamic form in which you wanted to show a field, but only if the user filled out a previous field prior with a certain value. Maybe we only want to show a checkbox if the user has selected ‘hot’ as a value in our radio field.

Using the getFormValues selector, we can select form values and use some vanilla JavaScript ternary logic to express our form output:

// src/modules/form/form.container.js:

import React from 'react';
import { connect } from 'react-redux';
import { reduxForm, getFormValues } from 'redux-form';

import FormComponent from './form.component';

export const FormContainer = props => {
  const submitForm = (formValues) => {
    console.log('submitting Form: ', formValues);
  }

  return (
    <FormComponent
      formValues={props.formValues}
      change={props.change}
      onSubmit={submitForm}
      handleSubmit={props.handleSubmit}
    />
  );
}

const mapStateToProps = state => ({
  formValues: getFormValues('my-very-own-form')(state),
});
const formConfiguration = {
  form: 'my-very-own-form',
}

export default connect(mapStateToProps)(
  reduxForm(formConfiguration)(FormContainer)
);
// src/modules/form/form.component

import React from 'react';
import { Field } from 'redux-form';

import Text from '../components/text';
import Select from '../components/select';
import Radio from '../components/radio';
import Checkbox from '../components/checkbox';

export const FormComponent = ({ handleSubmit, onSubmit, formValues }) => {
  return (
    <div className="flex flex-column justify-center items-center">
      <h1>My Very own Form</h1>
      <form
        className="w-80"
        onSubmit={handleSubmit(onSubmit)}
      >
        ...
        <Field
          name="spiceLevel"
          label="Spice Level"
          component={Radio}
          options={{
            mild: 'Mild',
            medium: 'Medium',
            hot: 'hot'
          }}
        />
        {formValues && formValues.spiceLevel === 'hot' ? (
          <Field
            name="wantsFries"
            label="Would you like fries with that?"
            component={Checkbox}
          />
        ) : ''}

        <button
          type="submit"
          className="link br2 bg-blue white dim pa3 f6 sans-serif b--blue ba"
        >
          Submit
        </button>
      </form>
    </div>
  );
}

export default FormComponent;

And now, the ‘wantsFries’ field will only show if the user selects ‘hot’ for the ‘spiceLevel’ field:

Redux-form dependent field example 1

Redux-form dependent field example 2

A little thing to note with these fields is that the values will remain in Redux state even if they are not displayed. If you checked ‘wantsFries’, and then clicked ‘medium’ on ‘spiceLevel’, the ‘wantsFries’ field would disappear but the value would still stick in state.

This can be fixed with some plain old React/Redux/JS patterns by manually setting the value in the reducer as we want at certain lifecycle methods or other events, as we did in the datepicker.

Validation

I’ve always liked forms that validate on every key press rather than having to fill out everything, hit the submit button, wait for the response, and then go hunting for all the possible errors. You can do both in Redux Form, but this section will focus on real-time client side validation.

Redux Form gives you the option of passing in all of the validation functions into the Redux Form configuration object or passing validation functions to individual Field components. I prefer the second method more because you get finer control of your validation process, and I don’t like doing much with configuration.

To create a validator, make a function that takes in ‘value’ (passes the value of the field on every update), and returns an error depending on the logic you write. Validators also get passed values of other fields and props too, in case you ever need to write validation logic requiring more data on the form and fields. Let’s make a simple required validator that returns an error if there is no value:

// src/modules/form/form.validators.js

// validate : (value, allValues, props) => error [optional] #

export const required = (value) => {
  if (!value) {
    return 'This field is required!'
  }
};

And then you feed it into your Field component as another prop:

// src/modules/form/form.component.js

import { required } from './form.validators'
…
<Field
  name="firstName"
  label="First Named"
  component={Text}
  validate={required}
/>
...

You could also pass in an array of validators if you wanted more than one.

Inside the component, you’re going to want to create some logic as to what to do or display when an error on the field pops up:

// src/modules/components/text.js

import React from 'react';

export const Text = props => {

  return (
    <div className="mv4 w-100">
      <div className="b sans-serif pv2 w-100">
        {props.label}
      </div>
      <input
        {...props.input}
        placeholder={props.label}
        type="text"
        className="pa2 ba b--black-40 w-100"
      />
      {props.meta && props.meta.error && props.meta.touched && (
        <div className="sans-serif red">
          {props.meta.error}
        </div>
      )}
    </div>
  );
}

export default Text;

Redux-form validation example

Submission

You can do a lot of things upon submit because it’s all just plain actions and reducers. All the patterns you’re used to in React and Redux can be applied here. You can integrate form submission with redux-sagas, redux-observables, thunks and promises, or whatever you desire. In our example, we used the built in handleSubmit helper that takes an onSubmit prop, which we defined ourselves.

What handleSubmit does is it runs validation on the form and if valid it calls onSubmit with the contents of the form data. What onSubmit does is completely up to you.

There may be instances where you need more flexibility in form submission, and in such cases I recommend throwing out handleSubmit and using the built-in action creators and selectors to dispatch actions to start form submission, run validation, and perform asynchronous side effects, all of which can be defined by yourself.

Conclusion

We made form widgets, learned how to use some built-in action creators and selectors, and made a form with Redux Form version 7.

I hope you can see how robust Redux Form can be and how nice it plays with standard React and Redux patterns. Next time you need to integrate a bunch of forms, give Redux Form a shot.