Tutorial

Managing Form State in React With Redux Form

Published on December 12, 2019
author

Yomi Eluwande

Managing Form State in React With Redux Form

Introduction

redux-form is a great way of managing forms that are powered by Redux. It is a Higher-Order-Component (HOC) that uses react-redux to make sure HTML forms in React use Redux to store all of its state.

redux-form has the following components to help you build your applications:

  • formReducer(): This is a function that tells how to update the Redux store based on changes coming from the application; those changes are described by Redux actions. formReducer has to be mounted on the Redux state at form.

  • reduxForm(): The reduxForm() function is a higher-order component takes a configuration object and it always returns a new function. It is used to wrap the form component and bind user interaction to the Redux dispatch actions.

  • The <Field/> component: A component that lives inside your wrapped form component. It serves as a way to connect the input elements in a form to the redux-form logic. In other words, it’s the way we get the input from what users type.

You can read more about the redux-form API in its documentation.

In this tutorial, you’ll use redux-form to build a form with validation and connect it to the Redux store.

Prerequisites

To complete this tutorial, you will need:

Step 1 – Creating the Project

We’ll be building a React app with the create-react-app package. create-react-app allows you to create React apps with no build configuration. You can use create-react-app by running the terminal command that follows. It automatically creates a React app for you in a folder titled contact-redux.

  1. npx create-react-app contact-redux

It’s important to note that npx only works with versions of npm that are 5.2 and above. If you have a version lower than that and would still like to use create-react-app on your computer. Run the terminal commands below to install create-react-app and start a React app.

  1. npm install -g create-react-app
  2. create-react-app contact-redux

Navigate to the directory and start the development server to ensure everything works. Run the following command to start the newly created React app in development mode:

  1. npm start

You’ll see the following in your browser:

The React app running in the browser

We now have a React app up and running.

Execute the following command to add the dependencies you’ll need for the form.

  1. npm install --save redux react-redux redux-form
  • redux - A state container and it’s a prerequisite for redux-form to work.
  • react-redux - React Redux is the official React bindings for Redux and it’s also a prerequisite for redux-form to work
  • redux-form - The package in use for this tutorial.

Once that’s been installed, you can work on the contact form.

Step 2 – Creating the Form

We’ll add a Bulma CDN link to the index.html file so as to add some default styling. Open the public/index.html file and add the following line of code to the head tag:

public/index.html
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">

We’ll be making some edits to the src/App.js file now. Open up the src/App.js file and add the line of code below at the top of the file.

src/App.js
import { reduxForm, Field } from 'redux-form';

Next, go to the render() function, and modify it with the following code:

src/App.js
render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React x redux-form</h1>
        </header>
        <div className="container">
          <p className="App-intro">
            Contact Form
          </p>
          <SignInForm />
        </div>
      </div>
    );
  }

The introductory text has been changed and most importantly we added a <SignInForm /> component which we’ll create below. It’s going to be a simple component that returns the form we need and it will be hooked up to redux-form component. In the same src/App.js file, type out this code below just before the declaration of class App extends Component.

src/App.js
let SignInForm = props => {
  return <form className="form">
    <div className="field">
      <div className="control">
        <label className="label">First Name</label>
        <Field className="input" name="firstName" component="input" type="text" placeholder="First Name"/>
      </div>
    </div>

    <div className="field">
      <div className="control">
        <label className="label">Last Name</label>
        <Field className="input" name="lastName" component="input" type="text" placeholder="Last Name"/>
      </div>
    </div>

    <div className="field">
      <div className="control">
        <label className="label">Email</label>
        <Field className="input" name="email" component="input" type="email" placeholder="Email Address"/>
      </div>
    </div>

    <div className="field">
      <div className="control">
        <label className="label">Proficiency</label>
        <div className="select">
          <Field className="input" name="proficiency" component="select">
            <option />
            <option value="beginner">Beginner Dev</option>
            <option value="intermediate">Intermediate Dev</option>
            <option value="expert">Expert Dev</option>
          </Field>
        </div>
      </div>
    </div>

    <div className="field">
      <div className="control">
        <label className="label">Age</label>
        <Field className="input" name="age" component="input" type="number" placeholder="Age"/>
      </div>
    </div>

    <div className="field">
      <div className="control">
        <label className="checkbox">
          <Field name="saveDetails" id="saveDetails" component="input" type="checkbox"/>
          Save Details
        </label>
      </div>
    </div>

    <div className="field">
      <div className="control">
        <label className="label">Message</label>
        <Field className="textarea" name="message" component="textarea" />
      </div>
    </div>

    <div className="field">
      <div className="control">
        <button className="button is-link">Submit</button>
      </div>
    </div>

  </form>;
};

In this code, we set up a minimal contact form, which asks the user for information such as First Name, Last Name, and Age. The interesting bit in this form is the Field component.

The Field component comes from the redux-form package and it’s how we write the input field. The type prop indicates what type of input it should be, that is, a radio input, a checkbox input, a text input or an email input. The component prop determines what type of input field it should be, it could be input, textarea or select tags and the name prop is what will be used to identify the state of the fields in the redux store which we’ll create below.

So in order to use the form hooked up to redux-form, we need to have some sort of Redux store created already and that’s what we’re going to do next.

Step 3 – Setting up a Redux Store

We need a Redux store in which we can connect the form component (SignInForm) we created. Let’s start by importing the redux package. Open the src/index.js file and add the following lines of code in which we are basically importing redux to the React app.

src/index.js
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import { reducer as formReducer } from 'redux-form';

The first line of code imports createStore and combineReducers. createStore helps to create a Redux store that holds the complete state tree of your app and the combineReducers helps to manage all of your reducer functions into one single helper function which can then be passed into createStore. You can read more about these function on the Redux API reference page.

The second line of code imports Provider from react-redux. Provider helps to pass the state of the store to all container components in the app and we’ll demonstrate how that works later.

The third line of code imports reducer as formReducer and that’s what we’re going to use to hook up our form to the Redux store.

Next, we’ll create the actual Redux store and make sure it’s applicable to all container components by using the Provider component. Edit the src/index.js file with the following code block.

src/index.js
import React from 'react';
import ReactDOM from 'react-dom';

import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import { reducer as formReducer } from 'redux-form';

import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

const rootReducer = combineReducers({
  form: formReducer,
});

const store = createStore(rootReducer);

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

registerServiceWorker();

In the code block above, we use the combineReducers function to connect the formReducer from the form to the Redux store. It’s basically used to update any of our state in response to actions, which in this case, are changes to the form. The next line of code is used to create a store by using createStore from Redux.

This newly created store is then made available to all parts of the app with the help of Provider which is wrapped around the App component and it also accepts a prop of store which is the store that was created above.

Let’s go back to the form and finally connect it to the store.

## Step 4 – Connecting the Form to redux-form

We have our form component but it’s not connected to redux-form yet. Let’s fix that. Type out this code block below just before the class App extends Component and immediately after the declaration of the SignInForm presentational component.

src/index.js
SignInForm = reduxForm({
  form: 'signIn',
})(SignInForm);

In the code block above, SignInForm is made into a redux-connected form using the reduxForm Higher Order Component. This means that our form is now hooked up to the store. One thing to note is the config key form, it is used as an identifier and it’s used to provide a unique name for the form component. If they were multiple forms, then you’d need to use separate names so as to better manage their different states.

The next thing we have to do is configure what happens when we click on the Submit button. In an ideal app, you’d want to send data to a remote API or some databases but for the purpose of demonstrations, we’ll log the form data into the browser console. To do that we’ll need to get the form data from the props and store them somewhere.

Inside the SignInForm component, add the line of code below just above the return statement.

[label src/index.js]  
  const { handleSubmit } = props;
  return <form **onSubmit={handleSubmit}** className="form">

The props in the SignInForm form is destructured into handleSubmit. The handleSubmit function will then be used in the form as a handler for the onSubmit event when the submit button is clicked on.

Finally, inside the App component in the src/App.js file, we’ll create a function that logs the form data to the browser console. Add the code block below to the file just before the render() function.

src/App.js
  handleSignIn = values => {
		console.log(values);
	};

Then add the handleSignIn function as an event handler for the SignInForm component. redux-form automatically will ascertain that the data gotten from the form, which is essentially the SignInForm component should be logged to the console thanks to the handleSubmit function above.

src/App.js
    <SignInForm onSubmit={this.handleSignIn} />

You can now start the app by running the npm start terminal command. Fill out the form, click on submit and you’ll see the values logged to the console.

The output from the form displays in the console

Step 5 – Adding Validations

Validations are important when it comes to building forms and redux-form ships with some validation features and we’re going to implement that now. In the src/App.js file, type in the code block below.

src/App.js
const validate = val => {
  const errors = {};
  if (!val.firstName) {
    console.log('First Name is required');
    errors.firstName = 'Required';
  }
  if (!val.lastName) {
    console.log('Last Name is required');
    errors.lastName = 'Required';
  }
  if (!val.email) {
    console.log('email is required');
    errors.email = 'Required';
  } else if (!/^.+@.+$/i.test(val.email)) {
    console.log('email is invalid');
    errors.email = 'Invalid email address';
  }
  if (!val.age) {
    errors.age = 'Required'
  } else if (isNaN(Number(val.age))) {
    errors.age = 'Must be a number'
  } else if (Number(val.age) < 18) {
    errors.age = 'Sorry, you must be at least 18 years old'
  }
  return errors;
};

The validate function is used to check for validation errors in the form. The val parameter will be used to check the validation of different fields. We first check if the errors object is empty, an empty object obviously means there are no errors. We then use conditional logic to check if a field is empty, if it is, you throw the corresponding error. In the code block above, we’re only doing validations for firstName, lastName, email, and age. In the email validation conditional, we check if it’s empty and also check if it’s a valid email by using regex and in the age validation conditional, we check if it’s empty, a number and if the user is less than 18.

Next, we’ll register our validation function to redux-form so that it can begin using it to carry out validation tests. Add the validate function to the redux-form Higher Order Component:

src/index.js

SignInForm = reduxForm({
  form: 'signIn',
  validate,
})(SignInForm);

Now that we have our validation function ready and registered in the redux-form HOC, build a reusable component that displays errors whenever there are any and using that newly created component in our forms.

src/index.js


const renderField = ({ input, label, type, meta: { touched, error, warning } }) => (
  <div>
    <div className="control">
      <label className="field">{label}</label>
      <input className="input" {...input} placeholder={label} type={type}/>
      {touched && ((error && <span>{error}</span>) || (warning && <span>{warning}</span>))}
    </div>
  </div>
)

The renderField component takes in props of the input object, a label, the type of input and meta which is a redux-form property. The line {touched && ((error && <span>{error}</span>) || (warning && <span>{warning}</span>))} means that an error message should show if there are any errors when the form field has been clicked/focused on. Also, the form will not be submitted if there are any errors.

Now, if you check the form and try to enter any invalid input or skip the fields that have validation tests, you should error messages underneath the form.

The completed project with validation messages

Conclusion

In this tutorial, you built a form with redux-form and connected it to the Redux store. You added synchronous validation on the form without the need for external schema validators.

You can read more about redux-form on the official site and you can go through their examples to explore further.

You can find the completed code for this tutorial on GitHub.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors
Default avatar
Yomi Eluwande

author

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Become a contributor for community

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

DigitalOcean Documentation

Full documentation for every DigitalOcean product.

Resources for startups and SMBs

The Wave has everything you need to know about building a business, from raising funding to marketing your product.

Get our newsletter

Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

New accounts only. By submitting your email you agree to our Privacy Policy

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.