Build a React/Redux Contact Form
Prerequisite: An Understanding of how React and Redux works.
Constructing HTML Forms that work can be a pain, and while building this web application I left implementing the functionality of my contact form to the last item on my to-do-list. Choosing to do so was not intentional, maybe consciously I felt it would take a lot of time for me to implement properly with the new technologies I was using and indeed it took time. In this article, I would take you through the following.
- Basic Understanding of redux-form and its functions.
- Installing the necessary packages to get your form to work with your app.
- Creating the HTML Form.
- Setting up a redux store for your form
- Connecting the form to the store
- Renderfield & Validating the form
- Conclusion
BASIC UNDERSTANDING
A redux form is a Higher Order Component, that is a component that takes another component and returns a new component. It uses react-redux to maintain the state of an HTML form.
Some of the predefined functions that come with redux form include formReducer, reduxForm and Field.
FormReducer is a function that regulates updates to the redux store state based on the information being passed by the redux dispatched actions from the application. The formReducer should be attached to the form in the rootReducer file.
reduxForm is also a Higher Order Component, it is used to wrap the HTML form and bind its interactions with redux dispatch actions
Fields serves as a listener to user input to the form.
INSTALLING PACKAGES
I used yarn to download my packages, another option would be to use npm, but I prefer yarn. We would be installing three packages at once, redux, redux-form and react-redux. To do this type in the following in your command line terminal
yarn add react react-redux redux-form
CREATING THE HTML FORM
Using a Bulma CDN link placed between the head tags in my index.html file to help style my contact form, create a Contact.js file and insert the following code.
import React from 'react';
import { Field, reduxForm } from 'redux-form';
class ContactMe extends React.Component{
constructor(){
super();
}
render() {
return (
<div className="column is-half">
<div className='tl pa3 w-40 leftZone' id='contactBox'>
<h1 className='f1'>{'Contact Me'}</h1>
<main className="black-80">
<form autoComplete="off" ref="contactForm" >
<fieldset className="ba b--transparent ph0 mh0">
<div className="box-left">
<Field className="pa2 input-reset ba bg-transparent hover-bg-black hover-white w-100" label="Full Name" name="name" id="name" type="text"/>
</div>
<div className="box-left">
<label className="clip" name="email" htmlFor="email-address">Email</label>
<Field className="pa2 input-reset ba bg-transparent hover-bg-black hover-white w-100" name="email" label="Email" id="email" type="email" placeholder="Email Address"/>
</div>
<div className="cf">
<label className="clip" htmlFor="subject">Subject</label>
<Field className="fixposition pa2 input-reset ba bg-transparent hover-bg-black hover-white w-100" name="subject" label="subject" type="text" id="subject" placeholder="Subject"/>
</div>
<div className="cf">
<label className="clip" htmlFor="subject">Message</label>
<Field className="fixposition input-reset ba hover-bg-black hover-white textarea" name="message" type="text" id="message" component="textarea" placeholder="Message"/>
</div>
</fieldset>
<button type="submit" className="pseudoBtn fixposition" label="send">SEND</button>
</form>
</main>
</div>
</div>
)
}
}
In the code block below, the form request for user information such as name, email, subject and message. One of the obvious difference between this form and a normal HTLM form is the use of Field as input type declarator. It has a tag named type
which determines the type of input for that field e.g text, email, radio, checkbox
. The component prop is used to determine what type of input tag the field is e.g textarea, input, select
etc and in this case, I am calling a function renderfield instead which receives the type of input tag implicitly and renders an error or warning message based on user entries.
Setting up redux store for the contact form.
To attach a redux store to the form we would have to attach the whole app to a store. First, create a file named rootReducer in the folder named store (Assuming such a folder was created to maintain functionality for redux store). In this file is we would use the combineReducers to connect the form to the redux store, which basically updates the state based on any changes to the form actions.
rootReducer.js
import { combineReducers } from 'redux'
import {reducer as formReducer } from 'redux-form'
export const rootReducer = combineReducers({
form: formReducer
})
Now in index.js file where we would connect the store to the rest of our app with the help of a Provider
we would import the component rootReducer
and create a store passing the values to createStore
.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import Contact from './App';</span><span id="1885" class="ha hb ap cd hc b eg hk hl hm hn ho he r hf">// Redux Store
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import { rootReducer } from './store/rootReducer';</span><span id="6e8e" class="ha hb ap cd hc b eg hk hl hm hn ho he r hf">const store = createStore(rootReducer);
ReactDOM.render((
<Provider store={store}>
<App />
</Provider>
), document.getElementById('root'))
registerServiceWorker()</span>
Connecting the store to the form
At the end of your Contact.js file add ….
export default reduxForm({
form: 'contactMe'})(Contact);
Note the identifier form
is used to identify which form data is being manipulated, so in the case where you have multiple forms on a website, giving each form unique identifiers is advised. Now move onto what happens when the submit button is clicked. This would be proof that the form is successfully connected. For the purpose of demonstrating this works, I would be console logging the data sent from the form.
In Contact.js we would be destructuring props passed into it, so before the line return
and after the line render()
add the following
const { handleSubmit } = this.props;
Remember to also add to the form
<form autoComplete="off" onSubmit={handleSubmit} ref="contactForm" >
After this in whichever component Contact.js was called, here we are using a ContactPage.js file, we are going to be writing a function handleSubmit
to log the data to the console. So in this file just before render()
add the function as demonstrated. Note that the function is passed as a prop to Contact.
import React from 'react';
import Background from './layout/Background;
import Contact from './Contact';
class ContactPage extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit = values => {
console.log(values);
};
render() {
return(
<div className="columns">
<Background/>
<Contact onSubmit = {this.handleSubmit} />
</div>
)
}
}
export default ContactPage
The values passed from redux-form should be logged in your console
.
RenderField & Validating the form
Remember to import helper components written under this section in your Contact.js file
In order to display helpful messages in case the user failed to enter a field correctly, we can create a renderField.js
file to handle this.renderField.js
const renderField = ({ input, label, type, meta: { touched, error, warning } }) => {
return(
<div>
<div className="control">
<label className="clip" name={label} htmlFor={label}>{label}</label>
<input {...input} placeholder={label} type={type}/>
{touched && ((error && <span className="help is-danger is-size-6">{error}</span>) || (warning && <span className="help is-warning is-size">{warning}</span>))}
</div>
</div>
)};
export default renderField;
The values { input, label, type, meta: { touched, error, warning } }
are passed in implicitly from the form to renderField.js when placed like this in the form
<Field className="fixposition pa2 input-reset ba bg-transparent hover-bg-black hover-white w-100" name="subject" label="subject" component={renderField} type="text" id="subject" placeholder="Subject"/>
I have used this to display messages in an asynchronous manner with form inputs name, email and subject
. The data passed in can be used to fill in data for the label and input tag and then finally when displaying messages to the user. The line after input is basically saying when clicked or in focus, display the message if it has an error or warning. I illustrate an error here using the Bulma class is-danger
and warning using is-warning
to give a red or yellow effect to the display messages. Import the function to the Contact.js to avoid errors when trying to run your app.
Validating your form is also important for obvious reasons 😁, here we would write a simple function to validate our form. Create a file validate.js
and add the following block of code.
export const validate = val => {
const errors = {}
if (!val.name) {
errors.name = 'Required';
}
if (!val.subject) {
errors.subject = 'Required';
}
if (!val.email) {
errors.email = 'Required';
} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(val.email)) {
errors.email = 'Invalid email address';
}
if (!val.message) {
errors.message = 'Required';
}
return errors;
}
First, we instantiate an errors object to store errors for each field. After which if the input field is empty an error message is stored in the error object with the corresponding input identifier. For the email section, I have used some regular expressions to also check if certain characters are not included if they aren't then the email is invalid. At the end of all the checks, the error object is returned!
Add the validate component to your form by including another line in this section of your Contact.js file
export default reduxForm({
form: 'contactMe',
validate
})(ContactMe);
Conclusion
Finally, the end. We have created an HTML Form, set up a redux store and connected the store to the redux-form, validated the user inputs and displayed helpful messages for any input error.
You can visit my web page https://oshogunle.com/inbox to have a look of where this email series should eventually get to.
Continue building the form with 1 and 2. Dont forget to clap and share if you found this useful