How to Create a Redux-Form with Validation and Initialized Values

Updated on 8/17/2016

You want your React app to have an intelligent form that validates the data being input before sending it off to your API. You want it to be easy and straightforward to implement with the features you'd expect. Features like setting initial values of your fields. Luckily for you, Redux-Form makes all that a breeze. Even better, with the latest release candidate (Redux-Form 6.0.0rc3), you can upgrade to the latest version of React and not have those pesky unknown props errors bring your project to a screeching halt.

This tutorial is going to help you set up a Redux-Form that uses the latest syntax, and how to get that form set up with some simple validation and initial values. We'll be pretending that we're creating an "update user information" form for a hypothetical application. The form is going to have access to actions to make submissions, and we'll work under the assumption that we've stored our user information in the user reducer.

The complete source code for this tutorial can be found at this gist.

Install the Redux-Form 6.0.0 release candidate

We're going to be using the latest Redux version, 6.0.0-rc.3 for this tutorial. This is because with the changes made in React 15.2 (and later) standard releases of Redux-From don't work without throwing a plethora of bugs. Furthermore, the syntax of Redux-Form has been modified to streamline and simplify the process of creating and managing your forms. There's no point in learning a syntax that's going to be absolute as soon as the release candidate is published, so let's get a head start!

Open up your console and use NPM to install the Redux-Form release candidate:

npm install --save redux-form@6.0.0-rc.3  

Our tutorial will also be dependent on react and react-redux, so make sure you have the latest versions of those installed as well.

Setting up your component

Opening up a blank document we want to import the dependencies we're going to need to create our form. The form we're making is connected to our application state, and will have an awareness of the actions we've created elsewhere in the project. This allows our form to send values to an API or another service directly.

import React, { Component } from 'react';  
import { Field, reduxForm, initialize } from 'redux-form';  
import { connect } from 'react-redux';  
import * as actions from '../../actions';  

Next, let's scaffold out our component real quick:

class ReduxFormTutorial extends Component {

  //our other functions will go here

  render(
    return (
      <div>
          //our form will go here
      </div>
    )
  )
}

function mapStateToProps(state) {  
  return {
    user: state.user
  };
}

export default connect(mapStateToProps, actions)(form(ReduxFormTutorial));  

You'll notice that we brought in our application state (user) and set it as props at the bottom. This is going to allow us to initialize our form with data that's already defined in our state.

Defining your form

The first thing we want to do is define our form. So just underneath our dependencies, outside of the scope of the component, add the following:

const form = reduxForm({  
  form: 'ReduxFormTutorial'
});

Handling the validation of our form can get messy if we do it "inline" as part of the render function of our component. So to clean that up and make it reusable (plus easier to manage), create a const that returns the input and logic for any errors that our field receives:

const renderField = field => (  
    <div>
      <label>{field.input.label</label>
      <input {...field.input}/>
      {field.touched && field.error && <div className="error">{field.error}</div>}
    </div>
);

Let's also create a similar constant that will serve our select input:

const renderSelect = field => (  
  <div>
    <label>{field.input.label</label>
    <select {...field.input}/>
    {field.touched && field.error && <div className="error">{field.error}</div>}
  </div>
);

Now we need to define the redux-form required property handleSubmit inside the render function of the component. Without this, the form simply will not work at all, and you'll get a bunch of ugly errors.

const { handleSubmit } = this.props;  

It's time to start making up our form! Inside the render() function return the following example form. Make sure to use the <Field/> component imported from redux-form in place of <input />.

    return (
      <div>
        <form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>

          <Field name="firstName" type="text" component={renderField} label="First Name"/>
          <Field name="lastName" type="text" component={renderField} label="Last Name"/>
          <Field name="sex" component={renderSelect} label="Gender">
            <option></option>
            <option name="male">Male</option>
            <option name="female">Female</option>
          </Field>
          <Field name="email" type="email" component={renderField} label="Email" />
          <Field name="phoneNumber" type="tel" component={renderField} label="Phone Number"/>

          <button action="submit">Save changes</button>
        </form>
      </div>
    )

We now need something to happen when the onSubmit function is called. Generally, this would mean calling an action and sending that action the values from our form.

  handleFormSubmit(formProps) {
    this.props.submitFormAction(formProps);
  }

At this point, your form should function properly, that is if this.props.submitFormAction were to point to an existing action that we had created. However, our form doesn't have any sort of validation or initial data prefilled into the fields. And those are nice, we want those.

Initialize your form with data

For our example here we're pretending that our form handles updates to our user's information. We wouldn't want them to have to type in all of their information every time they switch email accounts or phone numbers. So we want the form to initialize with their existing information which can then be altered in whatever way they wish.

Inside our component, let's call a function we'll name handleInitialize when the component mounts.

  componentDidMount() {
    this.handleInitialize();
  }

Now, we can create our function and define the initial values. Afterward, we call the redux-form property initialize and pass it our data. The objects names must correlate with name property of our <Field />'s above.

  handleInitialize() {
    const initData = {
      "firstName": this.props.currentUser.firstName,
      "lastName": this.props.currentUser.lastName,
      "sex": this.props.currentUser.sex,
      "email": this.props.userEmail,
      "phoneNumber": this.props.currentUser.phoneNumber
    };

    this.props.initialize(initData);
  }

It's as easy as that. When the component mounts, it will define our values and push it to the form's fields.

Adding form validation

The last thing we want to do now is add in some sort of form validation to make sure all the fields have something in them and it's of the appropriate format. Outside of our component, we want to define a function called validate that will take our formProps and run them through various tests.

function validate(formProps) {  
  const errors = {};

  if (!formProps.firstName) {
    errors.firstName = 'Please enter a first name';
  }

  if (!formProps.lastName) {
    errors.lastName = 'Please enter a last name';
  }

  if (!formProps.email) {
    errors.email = 'Please enter an email';
  }

  if (!formProps.phoneNumber) {
    errors.phoneNumber = 'Please enter a phone number'
  }

  if(!formProps.sex) {
    errors.sex = 'You must enter your sex!'
  }

  return errors;
}

We're going to make a quick jog back up to where we defined our form and add one more line to check our properties against our validation criteria. This process will also check our fields against HTML validation that was defined when we set the <Field /> property type to "email" (for example).

const form = reduxForm({  
  form: 'ReduxFormTutorial',
  validate
});

And there you have it! Your redux-form is now set to initialize with values, validate itself before submitting, and then pass it's approved values to your action. Redux-form is truly one of the most pivotal dependencies you will integrate into your application so getting a strong understanding of it is important.

That's all for now and thanks for reading! Once again, you can view the complete source code here. And if you have any questions, comments, or want to suggest a topic for me write about next, just leave it in the section below!

David Meents

React.js developer, web designer, and business owner.

Subscribe to David Meents

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!
comments powered by Disqus