Looking forward to learn React Redux example with axios? You have come to right place. In the last post, we have seen the working app on the client-side using React and Redux. In this post, we will Store the data on the MongoDB database and use Express as a web framework.

Async action in Redux is significant because, in a real-time application, you always have a backend, and somehow you need to figure out how we can use Redux with backend service. We will use the Axios: promise based library to send a network request to the Node.js web server.

What is Redux

Redux is a predictable state container for JavaScript applications.

Redux is also following the Unidirectional flow, but it is entirely different from Flux. Flux has multiple stores.

The fundamental building blocks of Redux are action, reducers, middleware, and the store. Having strong knowledge of these building blocks is required to be proficient with the Redux and Redux toolkit.

Consider the simple scenario:

“User clicks a button named submit, and a modal should appear soon after”.

Even in this component interaction, there is a state we must deal with.

For example, we can describe an initial state of the app as a plain JavaScript object.

const state = { buttonClicked: false, modalOpen: false }

When the user clicks the button, the state changes, and the updated state is following.

const state = { buttonClicked: true, modalOpen: true }

If you worked with React before, then the term state should be no surprise to you.

A stateful React component is the JavaScript class.

In a React component, the state holds the data that can be rendered to the user.

The state in React.js could also change in response to actions and events: in fact, you can update the local component’s state using this.setState() function.

So, in general, the typical JavaScript application is full of state. For example, the state is:

What the user can see (data) The data we fetch from the external API The URL The items selected inside the page Errors and exception show to the user

Having seen the basics, let’s talk about what problem Redux tries to solve?

So, the state is everywhere in web applications.

From now on, I will talk about state and logic in the context of the typical React application, but consider these concepts apply to any frontend architecture, regardless of the particular library.

The thing is that can you imagine how much state a web application has?

Even the simple single-page app could grow out of control without clear boundaries between every layer of the application. This holds particularly true in the React application.

Yeah, you can get by with keeping a state within the parent React component (or in context) as long as an application remains small.

Then some things will become tricky, especially when you add more behaviors and functionalities to the app.

At some point in time, you may want to reach for a consistent way to keep track of state changes.

Not only, but I would also say that the frontend components shouldn’t know about the business logic. Ever.

Unfortunately, the ton of logic gets stuffed into frontend components these days.

Is there an alternative to this problem? Now, Redux comes into the picture.

Redux can solve precisely those issues. It might not be apparent in the beginning, but Redux helps to give each frontend component the exact piece of the state it needs.

Even better, Redux can hold the business logic inside its layer (middleware), alongside with the code for fetching data.

The benefits of this approach are manifold.

Redux Middleware

A Redux middleware is a function that can intercept, and act according to requirement, our actions before they reach the reducer. And while the theory is quite simple, the Redux middleware can look a bit confusing.

In its basic form, a Redux middleware is the function returning a function, which takes next as the parameter. Then the inner function returns another function that takes action as a parameter and finally returns next(action).

Middlewares in Redux are super important because they will hold the much of your application’s logic.

If you think about it, there is no better place than a middleware for abstracting away business logic. And the nice thing is that while inside the middleware you can access the getState and dispatch.

The middleware we’re going to build should inspect the action’s payload.

There are a lot of benefits from using the Redux middleware:

Most of the logic can live outside the User Interface library. The Middlewares become reusable pieces of logic, easy to reason about. The Middlewares can be tested in isolation.

We use redux-thunk in this react-redux example as middleware for async action.

If you do not know what Redux thunk is then check out Redux thunk example.

It is a quite famous library among the developers. For the first time, all the things look pretty complicated, but trust me, one step at a time will solve all your doubts and confusion.

Redux is pretty complicated at first sight, and in this tutorial, redux-thunk is also there, so bear with me.

We will figure it out how we can join all of these concepts and build a simple React-Redux Axios Tutorial.

If you have not followed my previous tutorial on the client-side, then you can check out the below link.

Related Post: How To Connect React And Redux With Example

Let’s start with React Redux Example For Beginners.

The workflow of this demo

At the start of the application, we fire an action that fetches all the posts from the MongoDB database; if the data is not there, then we display no posts. When we add a new post, then first, it fires an action that sends a POST request to the node.js server, and Express saves the data in the database and returns that data in the JSON format We get the json data when the promise resolves, and then we fire another action that calls the reducer function and saves the data in the Redux store. So, when we change the state using the Redux, the UI will change, and we get our first post at the front end. As we know, we use the database so that data will be persisted and you can verify that by refreshing the page, you get the same data. We do the same for the delete, when we click the button, first it will send a delete request to the server and delete the document from MongoDB, and then we pass the id to reducer function and filter it out our posts as well.

React Redux Axios Example

Redux is a great tool that solves one of the main problems of UI frameworks: state management.

State management on the client-side can quickly grow into a nightmare, and the unidirectional flow of data Redux enforces makes it easy to understand how events alter the state of your application.

Sadly, state management is just one of the many issues you have to deal with while building robust applications.

Building a single-page CRUD app using React and Redux can be challenging because you’ll have to deal with new techniques and terms like “Reducers”, “Actions”, “Middlewares”, “Stores” and so on.

For medium to more significant projects, I always found Redux (or an equivalent) almost mandatory: state management libraries beautifully keep logic and behaviors abstracted away from the UI.

UI testability skyrockets and so developer productivity.

But if you look at it from a different perspective, you may argue that the Redux has its cost. It adds another layer of abstraction to your application.

We start our project by installing React.js.

Step 1: Install React.js.

Type the following command.

npm install -g create-react-app create-react-app postreactredux

Step 2: Install Redux, react-redux, and redux-thunk.

Type the following command to install both of the libraries.

npm install redux react-redux --save # or yarn add redux react-redux

We also need a redux-thunk library for async actions. Let us install using the following command.

npm install redux-thunk --save

Install Bootstrap 4. We will use React with Bootstrap 4.

npm install bootstrap --save

Now, import this file inside src >> index.js.

// index.js import '../node_modules/bootstrap/dist/css/bootstrap.min.css';

Step 3: Create a NewPost component.

If using react and redux together, then separation of concern is there. Means there are two types of components.

Dumb Components(Presentational) Smart Component(Containers)

Dumb components render the data, and they did not care about any logic. They have nothing to do with a redux store.

Smart components are concern about logic and directly connected to the store.

Now, inside the src folder, create one file called NewPost.js.

// NewPost.js import React from 'react'; class NewPost extends React.Component { state = { title: '', body: '' }; handleInputChange = e => { this.setState({ [e.target.name]: e.target.value }); }; handleSubmit = e => { e.preventDefault(); if (this.state.title.trim() && this.state.body.trim()) { console.log(this.state); this.handleReset(); } }; handleReset = () => { this.setState({ title: '', body: '' }); }; render() { return ( <div> <form onSubmit={ this.handleSubmit }> <div className="form-group"> <input type="text" placeholder="Title" className="form-control" name="title" onChange={ this.handleInputChange } value={ this.state.title } /> </div> <div className="form-group"> <textarea cols="19" rows="8" placeholder="Body" className="form-control" name="body" onChange={ this.handleInputChange } value={ this.state.body }> </textarea> </div> <div className="form-group"> <button type="submit" className="btn btn-primary">Add Post</button> <button type="button" className="btn btn-warning" onClick={ this.handleReset }> Reset </button> </div> </form> </div> ); } } export default NewPost;

So, this component has two form fields.

title body

When the user submits the form, we can see both the form fields value inside the console.

Okay, now we need to import this NewPost.js file inside the src >> App.js file.

// App.js import React, { Component } from 'react'; import NewPost from './components/NewPost'; import '../node_modules/bootstrap/dist/css/bootstrap.min.css'; class App extends Component { render() { return ( <div className="container"> <div className="row"> <div className="col-md-6"> <NewPost /> </div> <div className="col-md-6"> Display Post </div> </div> </div> ); } } export default App;

Save the file and start the development server by the following command.

Step 4: Create Node.js, Express, MongoDB backend.

First, install all the node.js dependencies using the following command.

npm install express body-parser mongoose --save

Also, we need a nodemon server for dev dependency because we do need to restart the server automatically after saving the code.

npm install nodemon --save-dev

I am not explaining the step by step guide on how to create a backend, do what I say to build a backend. Okay, now, inside the root of the React.js project, we need to create a new folder called server.

Inside the server folder, create one file called server.js and write the following code.

I am writing the final server.js file, so do not start the node server now because some files will not there until we create it.

// server.js const express = require('express'), path = require('path'), bodyParser = require('body-parser'), cors = require('cors'), mongoose = require('mongoose'), config = require('./config/DB'); const app = express(); mongoose.Promise = global.Promise; mongoose.connect(config.DB).then( () => {console.log('Database is connected') }, err => { console.log('Can not connect to the database'+ err)} ); const postroutes = require('./routes/PostRoute'); app.use(bodyParser.json()); app.use(cors()); const port = process.env.PORT || 4000; app.use('/posts', postroutes); const server = app.listen(port, function(){ console.log('Listening on port ' + port); });

Inside the server folder, create three more folders.

models routes config

Inside the config folder, create one file called DB.js. Write the following code in it.

// DB.js module.exports = { DB: 'mongodb://localhost:27017/posts' };

Inside the models folder, create one file called Post.js and write the following code inside that file.

// Post.js const mongoose = require('mongoose'); const Schema = mongoose.Schema; // Define collection and schema for Post let Post = new Schema({ title: { type: String }, body: { type: String } },{ collection: 'posts' }); module.exports = mongoose.model('Post', Post);

Inside the routes folder, create one file called PostRoute.js. Write the following code inside it.

// PostRoute.js const express = require('express'); const app = express(); const PostRoute = express.Router(); // Require Post model in our routes module let Post = require('../models/Post'); // Defined store route PostRoute.route('/add').post(function (req, res) { let post = new Post(req.body); post.save() .then(post => { res.status(200).json(post); }) .catch(err => { res.status(400).send("unable to save to database"); }); }); // Defined get data(index or listing) route PostRoute.route('/').get(function (req, res) { Post.find(function (err, posts){ if(err){ console.log(err); } else { res.json(posts); } }); }); // Defined delete | remove | destroy route PostRoute.route('/delete/:id').get(function (req, res) { Post.findByIdAndRemove({_id: req.params.id}, function(err, post){ if(err) res.json(err); else res.json(req.params.id); }); }); module.exports = PostRoute;

So, our backend code is complete; now, we need to start two servers.

Node.js server MongoDB server

Start the MongoDB server using this command.

mongod

Go to the root of the React.js project and start the Node.js server using the following command.

nodemon server/server.js

If all of your configurations are proper, then you can see our Node.js application is connected to the MongoDB database. Now, from the client-side, we can send a GET or POST request to this node.js express server.

Step 5: Create actions.

Now, inside the src folder, create three more folders, and their names are following.

containers reducers actions

Redux reducers are, without a doubt, the most crucial concept in Redux.

Reducers produce the state of an application. But how does the reducer know when to generate the next state?

The second principle of Redux says an only way to change the state is by sending the kind of signal or message to the store. This message or signal is an action. So “dispatching an action” means sending out the message to the store.

Confused? The reassuring thing is that the Redux actions are nothing more than JavaScript objects.

So, let’s create some actions.

Now, first inside the actions folder, create one file called types.js.

Write the following code inside it.

// types.js export const ADD_POST = 'ADD_POST'; export const DELETE_POST = 'DELETE_POST'; export const FETCH_POST = 'FETCH_POST';

These are action types.

When the page loads, we initiate the FETCH_POST action and fetch all the data from the server and save it inside the Redux store.

When the user submits the form, we need to call these actions. So when a user creates a post, we will call ADD_POST action.

This action then calls the reducer function and add the value to the store. So we can not directly modify the store, we need to create an action and then call the reducer function to alter the state of the store.

Same as a delete, when we try to delete any post, then DELETE_POST action will be triggered.

Now, that action returns an object that contains two properties.

Action type Payload

As we know for our demo, we have two actions, so create one file inside the src >> actions folder called index.js.

As you can see, it’s a JavaScript object with two properties: action type and payload.

The type property drives how the state should change, and it’s always required by Redux. The payload property instead describes what should change, and might be omitted if you don’t have new data to save in the store.

As a best practice in Redux, we wrap every action within a function, so that object creation is abstracted away. Such a function takes the name of action creator: let’s put everything together by creating a simple action creator.

Write the following whole code of index.js and explain it to you.

// index.js import { ADD_POST, DELETE_POST, FETCH_POST } from './types'; import axios from 'axios'; const apiUrl = 'http://localhost:4000/posts'; export const createPost = ({ title, body }) => { return (dispatch) => { return axios.post(`${apiUrl}/add`, {title, body}) .then(response => { dispatch(createPostSuccess(response.data)) }) .catch(error => { throw(error); }); }; }; export const createPostSuccess = (data) => { return { type: ADD_POST, payload: { _id: data._id, title: data.title, body: data.body } } }; export const deletePostSuccess = id => { return { type: DELETE_POST, payload: { id } } } export const deletePost = id => { return (dispatch) => { return axios.get(`${apiUrl}/delete/${id}`) .then(response => { dispatch(deletePostSuccess(response.data)) }) .catch(error => { throw(error); }); }; }; export const fetchPosts = (posts) => { return { type: FETCH_POST, posts } }; export const fetchAllPosts = () => { return (dispatch) => { return axios.get(apiUrl) .then(response => { dispatch(fetchPosts(response.data)) }) .catch(error => { throw(error); }); }; };

If a component is loading an object(e.g., list of Posts) via AJAX call to the server, that object’s state should keep track of all the potential states. Initial state for such objects should look like: {objName: {obj:null, loading: false, error:null}}.

Here, we have defined the sync and async actions.

Sync action returns an object that contains action type and payload.

Async action send a network request to the server and wait for the promise to resolve. When the promise resolves, it fires a sync action with the action type and payload.

For our demo project, we need to fire async action three times to interact with the server.

For fetching the data. For storing the data in the MongoDB database. For deleting the data in the database.

Step 6: Create the root reducer and postReducer.

Now, inside the reducers folder, create one file called postReducer.js.

Write the following code inside it.

// postReducer.js import { ADD_POST, DELETE_POST, FETCH_POST } from '../actions/types'; export default function postReducer(state = [], action) { switch (action.type) { case ADD_POST: return [...state, action.payload]; case DELETE_POST: return state.filter(post => post._id !== action.payload.id); case FETCH_POST: return action.posts; default: return state; } }

This file contains pure functions and does not relate to backend service. Reducers must be pure functions.

So, if the action type is matched with fired action, then it will modify the store and change the current state.

Now, create an index.js file inside the reducers folder. Write the following code inside it.

// index.js import { combineReducers } from 'redux'; import posts from './postReducer'; export default combineReducers({ posts: posts });

Step 7: Configure the redux store.

Import the src >> reducers >> index.js reducer file inside src >> index.js file.

So, our final src >> index.js file looks like below.

As you can see, the store is the result of calling createStore, a function from the Redux library. createStore takes a reducer as the first argument, and in our case, we passed in rootReducer.

You may also pass an initial state to createStore, useful for server-side rendering and state preloading.

The most important concept to understand here is that the state in Redux comes from reducers. Let’s repeat: reducers produce the state of your application.

What’s a reducer? A Redux reducer is just a JavaScript function. It takes two parameters: the current state and action (more about actions soon).

In a typical React component, the local state might be mutated in place. In Redux, you’re not allowed to do that. The third principle of Redux (as outlined by its creator) prescribes that the state is immutable and cannot change in place.

In other words, the reducer must be pure. A pure function is one that returns the same output for the given input. But despite this terminology, reasoning about a reducer is not that hard.

Redux store methods

The Redux store has three important methods.

getState: For reading the current state of the application. dispatch: For dispatching an action. subscribe: For listening to state changes.

// index.js import React from 'react'; import ReactDOM from 'react-dom'; import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import { Provider } from 'react-redux'; import App from './App'; import rootReducer from './reducers'; import { fetchAllPosts } from './actions/index'; import registerServiceWorker from './registerServiceWorker'; const store = createStore(rootReducer, applyMiddleware(thunk)); store.dispatch(fetchAllPosts()); ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root')); registerServiceWorker();

The 3rd line in the code block above imports the createStore and applyMiddleware functions from redux. Then we import the thunk from redux-thunk.

Then we create the store but with an applied middleware.

Now, we are ready to make an actual network request.

I’ll be making use of the axios library for making network requests, but be free to replace that with any http client of your choice.

Initiating a network request is pretty easy with redux-thunk. You create an action creator like this (i.e., an action creator that returns a function).

So, we have created the store and apply redux-thunk middleware to the store. This middleware helps us to deal with Async action inside the store.

After creating the store, we have dispatched the action that fetches all the posts from the server and put it inside the Redux store.

So, when our app loads for the first time, we get the data if there is any in the database.

Step 8: Create a container component.

Redux is framework agnostic means you can use it with vanilla Javascript or with other frameworks like Angular Or with React.

There are bindings for joining together Redux with your favorite framework/library.

For React, there is react-redux, the library for which you need to learn just one method for now: connect. What does it do? Unsurprisingly it connects the React component with a Redux store.

You will use connect with two or three arguments depending on the use case:

A mapStateToProps() function (you can name it also “select”) A mapDispatchToProps() function

The mapStateToProps does precisely what its name suggests: it connects the part of the Redux state to the props of the React component. By doing so, the connected React component will have access to the exact part of the store it needs.

The mapDispatchToProps does something similar, but for actions. The mapDispatchToProps connects Redux actions to the React props. This way, the connected React component will be able to send signals or messages to the store.

Let’s create the container component.

Inside containers folder, create a component called CreatePost.js.

// CreatePost.js import { connect } from 'react-redux'; import { createPost } from '../actions'; import NewPost from '../components/NewPost'; const mapDispatchToProps = dispatch => { return { onAddPost: post => { dispatch(createPost(post)); } }; }; export default connect( null, mapDispatchToProps )(NewPost);

We have connected the NewPost component to the Redux store.

We have created the higher-order component of the NewPost.js file, and that is a CreatePost.js file.

Now, we import this CreatePost.js file inside the src >> App.js file.

// App.js import React, { Component } from 'react'; import CreatePost from './containers/CreatePost'; import '../node_modules/bootstrap/dist/css/bootstrap.min.css'; const stylesApp = { marginTop: 40 } class App extends Component { render() { return ( <div className="container"> <div className="row" style={ stylesApp }> <div className="col-md-6"> <CreatePost /> </div> <div className="col-md-6"> Posts </div> </div> </div> ); } } export default App;

Now, when the user submits the form, we can trigger an action, and that action call the reducer and modify the global state.

So, we can now access the action inside the NewPost.js file.

// NewPost.js import React from 'react'; class NewPost extends React.Component { state = { title: '', body: '' }; handleInputChange = e => { this.setState({ [e.target.name]: e.target.value }); }; handleSubmit = e => { e.preventDefault(); if (this.state.title.trim() && this.state.body.trim()) { this.props.onAddPost(this.state); this.handleReset(); } }; handleReset = () => { this.setState({ title: '', body: '' }); }; render() { return ( <div> <form onSubmit={ this.handleSubmit }> <div className="form-group"> <input type="text" placeholder="Title" className="form-control" name="title" onChange={ this.handleInputChange } value={ this.state.title } /> </div> <div className="form-group"> <textarea cols="19" rows="8" placeholder="Body" className="form-control" name="body" onChange={ this.handleInputChange } value={ this.state.body }> </textarea> </div> <div className="form-group"> <button type="submit" className="btn btn-primary">Add Post</button> <button type="button" className="btn btn-warning" onClick={ this.handleReset }> Reset </button> </div> </form> </div> ); } } export default NewPost;

Step 9: Display the Post.

Create a component inside the components folder called Post.js and write the following code inside it.

Post.js component is responsible for rendering out all the Posts.

// Post.js import React from 'react'; const styles = { borderBottom: '2px solid #eee', background: '#fafafa', margin: '.75rem auto', padding: '.6rem 1rem', maxWidth: '500px', borderRadius: '7px' }; export default ({ post: { title, body, _id }, onDelete }) => { return ( <div style={ styles }> <h2>{ title }</h2> <p>{ body }</p> <button className="btn btn-danger" type="button" onClick={() => onDelete(_id)}> Remove </button> </div> ); };

So, this component only accepts the data of title, body, and _id and render it.

It also accepts the onDelete() function that can trigger the delete action, and then that action calls the postReducer function and deletes the post and state has been updated, and also, our UI will be updated.

Now, inside the containers folder, create one container component called PostList.js file and write the following code.

// PostList.js import React from 'react'; import { connect } from 'react-redux'; import Post from '../components/Post'; import { deletePost } from '../actions'; function PostList({ posts, onDelete }) { if(!posts.length) { return ( <div> No Posts </div> ) } return ( <div> {posts.map(post => { return ( <Post post={ post } onDelete={ onDelete } key={ post._id } /> ); })} </div> ); } const mapStateToProps = state => { return { posts: state.posts }; }; const mapDispatchToProps = dispatch => { return { onDelete: id => { dispatch(deletePost(id)); } }; }; export default connect( mapStateToProps, mapDispatchToProps )(PostList);

Now, this component gets the latest state from the store. When the new post is added, this component is notified because this component is directly connected to the store.

If the delete action is triggered, then it will filter out that post and display the remaining posts.

Now, the final thing is to import this PostList.js component inside the src >> App.js file.

// App.js import React, { Component } from 'react'; import CreatePost from './containers/CreatePost'; import PostList from './containers/PostList'; import '../node_modules/bootstrap/dist/css/bootstrap.min.css'; const stylesApp = { marginTop: 40 } class App extends Component { render() { return ( <div className="container"> <div className="row" style={ stylesApp }> <div className="col-md-6"> <CreatePost /> </div> <div className="col-md-6"> <PostList /> </div> </div> </div> ); } } export default App;

Save the file and go to the http://localhost:3000/

If you have not started the React development server, then start a server by this command: yarn start

If everything configured correctly, then we can be able to add, display, and delete the post.

I have put this code on Github if you find any error during the execution of this project, then please check out my Github code, it might be helpful to you.

Github Code

Steps To Use Code

Clone the repository. Go to the project folder and install dependencies using this command: npm install Start the mongodb server using this command: mongod Start the Node.js server using this command: nodemon server/server Start the Reacyt.js dev server using this command: yarn start

Finally, React Redux Axios Example is over. Thanks for taking it.

Related Posts

React Redux Authentication Example

Redux Form Validation Example

Redux Contact Form Application