AppDividend
Latest Code Tutorials

React Redux Axios Tutorial Example From Scratch

2,146

Get real time updates directly on you device, subscribe now.

React Redux Axios Tutorial Example From Scratch is the topic, we discuss today. 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 backend and somehow you need to figure it out, how we can use Redux with backend service. We will use Axios promise based library to send a network request to the Node.js server.

We use redux-thunk middleware for async action. It is a quite famous library among the developers. At the first time, all the things look pretty complicated, but trust me, one step at a time will solve all your doubts and confusions. 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 PostHow To Connect React And Redux With Example

Workflow of this demo

  1. 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.
  2. 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 return that data in the JSON format.
  3. 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.
  4. So, when we change the state using the Redux, the UI will change, and we get our first post at the front end.
  5. 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.
  6. We do 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 Tutorial

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

React Redux Axios Tutorial Example From Scratch

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 the async actions. Let us install using the following command.

npm install redux-thunk --save

Install the 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 NewPost component.

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

  1. Dumb Components(Presentational)
  2. 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 src foldercreate 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.

  1. title
  2. 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 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.

Redux React Tutorial

 

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, 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 server folder, create three more folders.

  1. models
  2. routes
  3. config

Inside 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 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.

  1. Node.js server
  2. 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 the actions.

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

  1. containers
  2. reducers
  3. actions

Now, first inside 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 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.

  1. Action type
  2. Payload

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

I am writing the 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);
      });
  };
};

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.

  1. For fetching the data.
  2. For storing the data in the mongodb database.
  3. For deleting the data in the database.

Step 6: Create the rootReducer and postReducer.

Now, inside 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 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 Store.

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

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

// 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();

First, 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 post from the server and put it inside Redux store. So, when our app loads for the first time, we get the data if is there any in the database.

Step 8: Create a 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 NewPost.js file, and that is a CreatePost.js file.

Now, we import this CreatePost.js file inside 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 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 components folder called Post.js and write the following code inside it.

Post.js component is responsible for rendering out our 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 delete the post and state has been updated and also our UI will be updated.

Now, inside 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, a final thing is import this PostList.js component inside 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 adddisplay, and delete the post.

 

React Redux Example Tutorial

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

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

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

2 Comments
  1. Dennis says

    Good job)

    1. Krunal says

      Thanks!!

Leave A Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.