The Complete Magazine on Open Source

Developing a React Redux Application Using create-react-app

7.8K 2

This article explains the development of a React application, using Redux for state management, interfacing with the Express framework for data and using create-react-app as the frontend build pipeline.

React is a JavaScript library for building user interfaces, and can be used to build single page applications. It is focused on creating the V of MVC, i.e., the View layer. Using a concept called Virtual DOM, components can be rendered with a good performance. There is large amount of state that needs to be managed by single page applications. The state includes server responses, cached and locally created data, and various UI states. Redux is a popular framework for managing the state. The data displayed by the client side UI can be provided from a back-end server. Express is a commonly used Web framework based on Node.js. This article explains the development of a React application using Redux for state management and interfacing with the Express framework for data, along with using the create-react-app for setting up the development environment. The create-react-app seamlessly handles the front-end build pipeline, enabling us to focus on writing the application logic.

To get an understanding of this article, readers should know the basics of React, Redux, Express.js, and Node.js.

Contacts application
The concepts of developing an application using React, Redux and Express will be illustrated through a Contacts application. We will keep the application simple, as the main objective is to illustrate the various concepts in an easy to understand manner. Using the application, we will be able to view the Contacts list, add a new contact and delete a contact. We will also use redux-thunk, a middleware, to perform the action asynchronously.

Setting up the development environment
We will develop the application using Windows as the development machine.
1. Install Node.js from https://nodejs.org. Download the 32-bit or 64-bit binary depending upon the architecture of the system.
2. Install create-react-app, using which we will start building the Contacts application.

npm install –g create-react-app

3. Generate the basic code template, which we will modify to add the features required for the Contacts application.

create-react-app contacts

This will create a directory called contacts in the current folder. Inside that folder, we can see an initial project structure generated; the required dependencies are automatically installed.
4. Change to the directory contacts. Check if the set-up is proper, by running the basic application:

npm start

This will start the application in development mode and ‘Welcome to React’ will be displayed.
5. Install the modules for Redux integration:

npm install –S redux
npm install –S redux-thunk
npm install –S react-redux

6. Install the modules, toastr and jquery, to display status on the browser when performing UI actions.

npm install –S toastr
npm install –S jquery

7. Install the HTTP client module, axios, to get data from the server.

npm install –S axios

8. Install the express and body-parser modules to serve the persistent data required for the Contacts application.

npm install –S express
npm install –S body-parser

9. To proxy API requests during development and avoid CORS issues, update package.json present in the contacts folder with the proxy setting (assuming that the Express server runs at port 4000).

"proxy": "http://localhost:4000"

Now we can start working on adding the features required for the Contacts application. While explaining the implementation, main code snippets have been provided. For the complete code, please refer to the GitHub repository at https://github.com/srini-wip/react-redux-contacts.git.

Serving the data required by the Contacts application using Express
We will now implement the code to serve the data required by the Contacts application using the Express Web framework.

Create a file server.js under the folder contacts. To provide the list of contacts, we will implement the API endpoint /api/contacts using the GET method. This will read the JSON file, contacts.json, for the list of available contacts and send the response as JSON data.

app.get(‘/api/contacts’, function(req, res) {
// read from JSON file and send the response as JSON data
});

To add a contact to the contact list, we implement the API endpoint api/contacts using the POST method. To retrieve the data sent by the browser, we use the body-parser middleware, which inserts the required data in the request object, from which we can easily extract and save it to the JSON file.

app.post(‘/api/contacts’, function(req, res) {
// extract the data and save to JSON file
});

To delete a contact from the contact list, we implement the API endpoint api/contacts/:id using the DELETE method. This will delete the contact and update the JSON file.

app.delete(‘/api/contacts/:id’, function(req, res) {
// delete the contact and update the JSON file
});

Implementing the user interface using React and Redux
We will now implement the set of features on the client side using React and Redux. First, we will create the folder structure required for writing the code to manage the state and view. This will help us in organising our code better. We will create the folders store, reducers, actions, api, components under the src folder.

Configuring the store
Create a file configureStore.js in the folder store. Initialise store using the createStore API of Redux. We need to provide the rootReducer, initial state (if any) and then a store enhancer. Since we will use redux-thunk, to implement asynchronous actions, the last parameter will be the thunk middleware.

createStore( rootReducer, initialState, applyMiddleware(thunk) );

Implementing the reducers
Create a file contactReducer.js under the folder reducers. When the store dispatches an action, it passes to the rootReducer, the state maintained in the store and the action. While creating the rootReducer, we have combined all the individual reducers of the application using the combineReducers API. In our current application, we have only one reducer, i.e., for contacts, which we will create shortly. Although we have only one reducer, it will be useful to use the combineReducers API because we can extend it to add more reducers as our application expands in the future.

rootReducer = combineReducers({ contacts });

The rootReducer will pass the action and the respective state to each reducer. We will implement the reducer function contactReducer accepting two parameters—state and action. The reducer is supposed to handle the action it is interested in and return the new state. The important thing to understand is that the reducer is a pure function, and it should not mutate the parameters and return a new state in an immutable way using only the values passed in the parameters. In contactReducer, we will handle the actions – successful loading of contacts, successful addition of a contact and successful deletion of a contact.

export default function contactReducer(state=initialState.contacts, action) {
switch (action.type) {
case ‘LOADED_CONTACTS’:
// Return contacts

case ‘ADDED_CONTACT’:
// Add new contact to state and return

case ‘DELETED_CONTACT’:
// Delete contact from state and return

default:
// Return state passed as parameter
}
}

Implementing the actions
Create a file actionTypes.js in the folder actions to store all the action names as a constant. Having the action types as a constant will help in better maintenance of code rather than having them directly as a string.

Next, create a file contactActions.js, in which we will implement various actions related to Contacts. We will implement the actions—loading of contacts, adding a contact and deleting a contact. As we will communicate using the REST API calls with a server in the backend, we will make asynchronous calls and, hence, we will use the middleware, redux-thunk, to perform asynchronous dispatch. The thunk function will invoke the Contacts API (discussed below), which will communicate with the server and fetch or add/modify the data based on the action. The Contacts API will use the npm module axios to make REST API calls to the Express server. The Contacts API will perform the necessary task and return either success or error. If success is returned, then the corresponding success action —load, add or delete—will be dispatched, after which the reducer code discussed above will get executed.

export function loadContacts() {
return function(dispatch) {
// Invoke Contacts API to load contacts and process
// successful result or error
};
}

export function addContact(contact) {
return function(dispatch) {
// Invoke Contacts API to add contact and process
// successful result or error
};
}

export function deleteContact(id) {
return function(dispatch) {
// Invoke Contacts API to delete contact and process
// successful result or error
};
}

Implementing the API invocation
Create a file contactApi.js in the folder api. We will implement the APIs getAllContacts, saveContact and deleteContact. To implement the invocation of these APIs, we will issue REST API calls to the Express server using the npm module axios.

export default class ContactApi {
static getAllContacts() {
// Invoke REST endpoint ‘/api/contacts’ using axios.get
}

static saveContact(contact) {
// Invoke REST endpoint ‘/api/contacts’ using axios.post
}

static deleteContact(id) {
// Invoke REST endpoint ‘/api/contacts/<id>’ using axios.delete
}
}

Implementing the UI components
We will now use React to implement the UI components. Also, we will use the react-redux module to interface React with Redux.

Create a file ContactsComponent.js in the folder components. Here, we will implement the top-level React container component, which will interface with the Redux store. We will write the method render, which will return the JSX to generate the Virtual DOM for the Contacts display and also a form to get the inputs from the user for adding a contact. Also, each contact will render itself using a separate React component, ContactComponent. Then, we will write addContact and deleteContact methods, which will dispatch the add and delete actions we had discussed earlier.

We need to access the Redux store to receive the state changes and to dispatch actions. The React container component ContactsComponent will interface with the store using the connect API provided by the react-redux module. The connect API accepts two parameters – a function (which we will name mapStateToProps) that receives the state from the store, and another function (which we will name mapDispatchToProps) that receives the dispatch method. The return value of the connect API is a function to which we will pass the React container component ContactsComponent as a parameter, and then finally export it.

class ContactsComponent extends React.Component {
render() {
// Return the JSX to display Contact list and the Contact form
}
}

function mapStateToProps(state, ownProps) {
// Return an object containing state details
}

function mapDispatchToProps(dispatch) {
// Return an object containing action dispatch details
}

export default connect( mapStateToProps, mapDispatchToProps ) (ContactsComponent);

Create the file ContactForm.js under the folder components. We will implement the code to get the form inputs from the user for adding a contact. This will be a React presentation component as it need not directly interface with the store. It will pass the input information to the container component, ContactsComponent, which takes care of interfacing with the store.

export default class ContactForm extends React.Component {
render() {
// Return the JSX to display input elements
}
}

Create the file ContactComponent.js under the folder components. We will implement the code to render the contact information:

export default class ContactComponent extends React.Component {
render() {
// Return the JSX for a contact
}
}

Implementing the startup code
In the file index.js under the src folder, remove the code generated by create-react-app and add the code to implement the startup code of the application. We will invoke the configureStore function discussed earlier. Next, we will dispatch the action to load the contacts. Then we will render the top level ContactsComponent by wrapping it inside a Provider component provided by the react-redux module. We wrap it inside a Provider component to provide access to the Redux store through the connect API of react-redux, which we had discussed earlier.

store = configureStore();

store.dispatch(loadContacts());

// Render the Contacts React component by wrapping it in Provider

Updating index.html to include stylesheet
In the file index.html under the public folder, add the link to the bootstrap CSS for presentable UI and the stylesheet toastr.css to display UI status during add and delete actions.

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="css/toastr.css"/>

Remove the generated files that are not needed
Remove the files App.js, App.css, App.test.js, index.css and logo.svg generated by create-react-app as we do not need them.

Running the application
1. Go to the Node.js command prompt. Change the directory to contacts.
2. Start the server.

start node server.js

3. Start the React application Contacts:

npm start

This will start the application and display the Contacts information. We can now add and delete contacts.