Image Source

Creating React Redux Express Full Stack Application Part-II

Pushpraj Yadav
Walmart Global Tech Blog
7 min readDec 2, 2020

--

Introduction

We have already developed the application with Express backend server and React frontend in previous post. In this post we will be enhancing the React JS frontend to integrate with Redux library to connect seamlessly with backend.

Features of Redux (Image Source)

Why Should I Use Redux ?

Redux helps you manage “global” state — state that is needed across many parts of your application.

The patterns and tools provided by Redux make it easier to understand when, where, why, and how the state in your application is being updated, and how your application logic will behave when those changes occur. Redux guides you towards writing code that is predictable and testable, which helps give you confidence that your application will work as expected.

When Should I Use Redux ?

Redux is more useful when:

  • You have large amounts of application state that are needed in many places in the app
  • The app state is updated frequently over time
  • The logic to update that state may be complex
  • The app has a medium or large-sized codebase, and might be worked on by many people
React-Redux application data flow looks like this (Image Source)

Steps to develop React Redux application understanding common terminology in the process

Folder structure of the final project will look like this :-

Actions

An action is a plain JavaScript object that has a type field. You can think of an action as an event that describes something that happened in the application.

The type field should be a string that gives this action a descriptive name e.g. DATA_FROM_BACKEND. An action object can have other fields with additional information about what happened. By convention, we put that information in a field called dataFromBackend. A typical action object might look like this:

Action Creators

An action creator is a function that creates and returns an action object. We typically use these so we don’t have to write the action object by hand every time.

Dispatch

The Redux store has a method called dispatch. The only way to update the state is to call dispatch() and pass in an action object. The store will run its reducer function and save the new state value inside. This will cause UI to be rendered with new state value.

Reducers

A reducer is a function that receives the current state and an action object, decides how to update the state if necessary, and returns the new state: (state, action) => newState. You can think of a reducer as an event listener which handles events based on the received action (event) type.

Reducers must always follow some specific rules:

  • They should only calculate the new state value based on the state and action arguments
  • They are not allowed to modify the existing state. Instead, they must make immutable updates, by copying the existing state and making changes to the copied values.

Store

The current Redux application state lives in an object called the store .

The store is created by passing in a reducer.

Let’s apply above learnt logics to develop redux based React application :-

  1. Create basic React based application running concurrently with Node express backend as explained in previous blog

2. Update the contents of client/package.json as below . Redux based dependencies have been added.

{
"name": "react-redux-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-redux": "^7.2.1",
"react-scripts": "3.4.3",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"proxy": "http://localhost:5000/",
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]}}

3. After updating the package.json, run npm installcommand under client directory to install all the needed dependencies.

4. Add store.js file (client/src/store.js) to create a store for the React Redux application.

Redux Thunk middleware allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. The inner function receives the store methods dispatch and getState as parameters.

import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk' //Middleware
import appReducer from './reducers'
const store = createStore(appReducer,applyMiddleware(thunk));export default store;

5. Once store is created, create folder with the name reducers (client/src/reducers) to keep reducer files. Add 2 files in this reducers folder :-

  1. dashboardReducer.js (client/src/reducers/dashboardReducer.js)
export const initialState = {
dataFromBackend : {},
getDataFromBackend : {}
}
export const dashboardReducer = (state = initialState, action) => {switch(action.type){case 'DATA_FROM_BACKEND' :
return {
...state,
dataFromBackend : action.dataFromBackend
}
case 'GET_DATA' :
return {
...state,
getDataFromBackend : action.getDataFromBackend
}
default:
return state
}
}

2. index.js (client/src/reducers/index.js) — This file is used to combine multiple imported reducers (e.g. ./dashboardReducer)in case of complex application where more than one reducer file is present.

import {combineReducers} from 'redux'
import {dashboardReducer} from './dashboardReducer'
const config = {
dashboardReducer : dashboardReducer
}
const appReducer = combineReducers(config);
export default appReducer;

6. After adding reducers for the application, create actions folder (client/src/actions) to keep action creator files. Add action creator dashboardAction.js file (client/src/actions/dashboardAction.js) under this folder.

//Async action creator for POST API Routeexport const sendData = (url,payload) => {
return dispatch => {
return fetch(url,{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
}).then(res => {
return res.json();
}).then(res => {
dispatch({type : 'DATA_FROM_BACKEND', dataFromBackend : res })}).catch(err => {
console.log('API failed')
})}}
//Async action creator for GET API Routeexport const getData = (url) => {
return dispatch => {
return fetch(url).then(res => {
return res.json();
}).then(res => {
dispatch({type : 'GET_DATA', getDataFromBackend : res })}).catch(err => {
console.log('API failed')
})}}

7. Once we are ready with store, reducer and async action creator, let’s embed store in React application by updating code of index.js (client/src/index.js) file.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
//React-Redux
import store from './store'
import {Provider} from 'react-redux'
ReactDOM.render(<Provider store={store}>
<React.StrictMode>
<App />
</React.StrictMode>
</Provider>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

8. Update App.js file (client/src/App.js) such that React component is connected with store as Higher Order Component using connect library.

The connect() function connects a React component to a Redux store.The mapStateToProps and mapDispatchToProps deals with your Redux store’s state and dispatch, respectively. state and dispatch will be supplied to your mapStateToProps or mapDispatchToProps functions as the first argument.

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
//Redux components
import {sendData , getData} from './actions/dashboardAction'
import {connect } from 'react-redux'
import {bindActionCreators} from 'redux'
class App extends Component {
constructor(props){
super();
this.state = {
response: '',
post: '',
responseToPost: '',
};
this.handleSubmit = this.handleSubmit.bind(this);
this.callApi = this.callApi.bind(this);
}


componentDidMount() {
this.callApi();
}

callApi = () => {
this.props.getData('/api/hello');
};

handleSubmit = () => {
this.props.sendData('/api/data',{ post: this.state.post });
};

render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
<p>{this.props.getDataFromBackend.express}</p>
<div>
<p>
<strong>Post to Server:</strong>
</p>
<input
type="text"
value={this.state.post}
onChange={e => this.setState({ post: e.target.value })}
/>
<button onClick={this.handleSubmit}>Submit</button>
</div>
<p style={{color : 'blue'}}><b>{this.props.dataFromBackend.data}</b></p>
</div>
);
}
}
//React Redux connecting codefunction mapStateToProps(state){
return {
dataFromBackend : state.dashboardReducer.dataFromBackend,
getDataFromBackend : state.dashboardReducer.getDataFromBackend
}
}
const mapDispatchToProps = dispatch => bindActionCreators({
sendData,
getData
},dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(App);

Final folder Structure of your React-Redux-Express application will look like this :-

Running the App

After making above needed code changes, run npm start command at project root folder (react-redux-express-app)

npm start

This will launch the React app and run the server at the same time.

Now navigate to http://localhost:3000 and you will hit the React Redux application displaying the message coming from our GET Express route.

React Redux Application displaying data fetched from Node Express backend

Get the full source code on GitHub Repository

Conclusion

In this post we learnt how to Integrate Redux with React app to maintain a global state away from react components.

Previous Post: Creating React Redux Express Full Stack Application Part-I

Thank you for reading. Happy coding!

--

--