Prerequisite: Have a contact form that uses redux, gone through post 1 and 2
Add a new initialstate to a file that saves all the states of components we want to manipulate in our app. This file is normally inside your store directory and in my directory, it is named initialState.js. In this file add the following to this file
export default {
contact: {
statusCode: 0
}
}
From the post Build a React/Redux Contact Form I have introduced the rootReducer.js file. Import and add the contactReducer component, it should look like this.
import contact from './contact/contactReducer'
import {reducer as formReducer } from 'redux-form'
export const rootReducer = combineReducers(
contact,
form: formReducer
})
First, configure our redux store for the contact form. Create a folder for contact in your store directory. In there create three files contactTypes.js, contactReducer.js and contactAction.js. contactTypes.js Add the following to this file:
export const SEND_MAIL_SUCCESS = "SEND_MAIL_SUCCESS"
export const SEND_MAIL_FAILED = "SEND_MAIL_FAILED"
export const SEND_MAIL_RESET = "SEND_MAIL_RESET"
The types here help set up what kind of data you want to be handling when your form is submitted. So here I have three types of data I want to be looking out for and I have named them accordingly. contactAction.js Add the following to this file
import * as types from './contactTypes'
export function emailSent(code) {
return { type: types.SEND_MAIL_SUCCESS, code}
}
export function emailErrored(errCode) {
return { type: types.SEND_MAIL_FAILED, errCode}
}
export function emailResetState() {
return { type: types.SEND_MAIL_RESET}
}
In this file we write actions to pass data under a particular type we declared earlier. These action functions are called in the client side which would be shown further down in this post contactReducer.js Add the following to this file
import initialState from '../../store/initialState';
import * as types from './contactTypes';
export default function contactReducer(state=initialState.contact, action) {
switch(action.type) {
case types.SEND_MAIL_RESET:
return {
...state,
statusCode: ''
};
case types.SEND_MAIL_SUCCESS:
return {
...state,
statusCode: action.code
};
case types.SEND_MAIL_FAILED:
return {
...state,
statusCode: action.errCode
};
default:
return state
}
}
In contact reducer, we maintain and manipulate state. When one of the action functions is called it uses the type associated with that action to change the state of our contact form.
Client Side Configuration
If you completed the tutorial on building a contact form with React and Redux, then I would be referencing documents created in that post here.
In your ContactPage.js file you want to make the handleSubmit function more functional. First, install the package Axios by running yarn add axios. Axios, in a nutshell, handles HTTP request, you can read more from the link.
Replace with the following in the file ContactPage.js. Save the previous file and compare to understand the necessary changes where. Nevertheless, I would discuss them below.
import React from 'react';
import Background from './layout/Background';
import { connect } from 'react-redux';
import axios from "axios";
import {reset} from 'redux-form';
import ContactMe from './contact/ContactMe';
import * as actions from '../store/contact/contactAction';
class ContactPage extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
async handleSubmit(data, e) {
const form = await axios.post('inbox', data)
.then(response => {
if(response.data.message)
{
this.props.dispatch(actions.emailSent(response.status));
this.props.dispatch(reset('contactMe')); // requires form name
}
else if (response.data.statusCode)
this.props.dispatch(actions.emailErrored(response.data.statusCode));
})
.catch(error => {
console.log(error.response)
});
};
render() {
return(
<div className="columns">
<Background>
<ContactMe onSubmit = {this.handleSignIn} message={this.props.statusCode}/>
<Background/>
</div>
)
}
}
function mapStateToProps(state, ownProps) {
return {
statusCode: state.contact.statusCode
}
}
export default connect(mapStateToProps)(ContactPage)
In this file, we have imported connect, axios, reset, ContactMe, and all actions from out contactAction from out contactActions file. I would explain what I have done with these imports at each stage.
In the constructor, we have binded the handleSubmit file to the class and we have an async before the handleSubmit declaration because HTTP request is handled asynchronously, we wouldn't want our whole application to pause waiting for a response before we can do something else on the application. Axios, as discussed above, handles HTTP request, here when we get a response that is positive, that is the message was sent we receive data of the message that was sent if it was unsuccessful we receive an error code. Hence the if statement. If we received a message we want to update our initial statusCode state with a status code of 200 after which we reset the form by passing the name of the form we want to reset in this example contactMe. If we received a statusCode instead there is a possibility of an error and we dispatch the action emailErrored function.
I have connected the statusCode state to the component to enable me to pass the statusCode to the Contact.js component. With that, I can add a prop message to ContactMe aswell. Create a new component called Notification.js
import React from 'react'
const Notification = (message) => {
return (
<div className="emailMsg">
<div className={`notification ${ message.style }`}>{message.text}</div>
</div>
)
}
export default Notification
This function would serve as a box that holds our success or error messages. I have also passed in some styling depending on what we receive back from submitting the form. Now, let's use this. Go back to Contact.js Add the following.
import Notification from '../Notification';
After the constructor and before the render() function add this:
emailNotification = (message) => {
switch(message) {
case 200:
return <Notification text={'Message was sent successfully :)'} style={'is-warning'} />;
case 400:
return <Notification text={'Sending message errored!'} style={'is-danger'}/>;
case '':
return <Notification text={''} style={'is-whitesmoke'}/>;
default:
return null;
}
}
Add message to your destructured props and then place your emailNotification function in a suitable place in your contact.js component like so:
{this.emailNotification(message)}
There you go when you send emails if successful your contact form should be reset and a green message bar shows with a success message, if not your input will remain and you get an error message.
This concludes the tutorial of creating a contact form that works using React, Redux, Nodejs, Nodemailer and Mailgun. Hope it helped! 😊
Checked out the finished contact form at oshogunle.com/inbox
The code for the form is also tracked in my git repository github.com/Omotola28/myportfolio