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.
To complete this tutorial, you will need:
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
.
- 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.
- npm install -g create-react-app
- 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:
- npm start
You’ll see the following in your browser:
We now have a React app up and running.
Execute the following command to add the dependencies you’ll need for the form.
- npm install --save redux react-redux redux-form
Once that’s been installed, you can work on the contact 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:
<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.
import { reduxForm, Field } from 'redux-form';
Next, go to the render()
function, and modify it with the following code:
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
.
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.
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.
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.
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.
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.
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.
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.
<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.
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.
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:
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.
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.
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.
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.
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!