DEV Community

Cover image for Making API calls in React using Redux-Thunk
Muhammad Awais
Muhammad Awais

Posted on

Making API calls in React using Redux-Thunk

Deep dive into react-redux with a thunk as a middleware to make the API calls on-load that makes your app faster.

When working with Redux, you will need three main things:

  1. Actions: these are nothing but the objects that should have two properties, one describing the type of action, and one describing what should be changed in the app state.

  2. Reducers: these are functions that implement the behavior of the actions. They change the state of the app, based on the action dispatched.

  3. Store: it brings and connects the actions and reducers together, holding and changing the state for the whole app β€” there is only one store.

Redux lets your React components read data from a Redux store, and dispatch actions to the store to update data using reducers.

Let's deep dive into the redux:

  • npm i redux redux-thunk redux-persist redux-logger

redux > actions > actions.js

export const GET_USERS = "GET_USERS";
Enter fullscreen mode Exit fullscreen mode

redux > actions > taskAction.js

import {
    GET_USERS,
} from "./actions";

export const GetUsers = () => {
    console.log("GetUsers");

    return dispatch => {
        console.log("GetUsers dispatch");

        axios.get(`https://reqres.in/api/users`)
        .then(res => {
            const persons = res.data;

            dispatch({
                type: GET_USERS,
                users: response
            });
        })
    };
};

export const AddUser = (params) => {
    console.log("AddUser");

    return dispatch => {
        console.log("Add User dispatch");

        axios.post(`https://reqres.in/api/users`, {params})
        .then(response => {
            console.log(response);

            axios.get(`https://reqres.in/api/users`)
            .then(res => {
                console.log(res);

                dispatch({
                    type: GET_USERS,
                    users: response
                });
            })
        })
    };
};
Enter fullscreen mode Exit fullscreen mode

redux > reducers > index.js

import { combineReducers } from 'redux';
import TaskReducer from './taskReducer'; //add this line

const rootReducer = combineReducers({
  task:TaskReducer  //add taskreducer and name is task for future use.
  });
export default rootReducer;
Enter fullscreen mode Exit fullscreen mode

redux > reducers > taskReducer.js

import { 
    GET_USERS,
  } from "../actions/actions";

  const INITIAL_STATE = {
    Users: [],
    loading : false,
  };

  export default (state = INITIAL_STATE, action) => {
    //  console.log("task reducer"  , action);

    switch (action.type) {
      case GET_USERS: {
        return {
          ...state,
          Users: action.users,
          loading: false
        };
      }
      default:
        return state;
    }
  };
Enter fullscreen mode Exit fullscreen mode

redux > store > store.js

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from '../reducers/index';
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'

const persistConfig = {
   key: 'root',
   storage: storage,
}

const middlewares = [thunk];

if (process.env.NODE_ENV === `development`) {
   const { logger } = require(`redux-logger`);

   middlewares.push(logger);
}

const persistedReducer = persistReducer(persistConfig, reducers)

export default () => {
   let store = createStore(
     persistedReducer,
     applyMiddleware(...middlewares)
   )

   let persistor = persistStore(store)
   return { store, persistor }
}
Enter fullscreen mode Exit fullscreen mode

App.js should be look something like this,

import {
  GetUsers
} from "./app/redux/actions/taskAction";

 class App extends React.Component {

  constructor(props) {
      super(props);

      this.state = {
      }
  }

  componentDidMount() {
    // making all API calls and store in the redux-store
    this.props.GetUsers();
  }

  render() {
    console.log("this.props.tasksss ", this.props.Loading);
    return (
      <div>
      ...
      </div>
    );
  }
}

const mapStateToProps = state => ({
  Loading: state.task.loading
});

const mapDispacthToProps = dispatch => {
  return {
    GetUsers: () => dispatch(GetUsers())    
  };

};
export default connect(
  mapStateToProps,
  mapDispacthToProps
)(App);
Enter fullscreen mode Exit fullscreen mode

Apps main index.js should be look something like this,

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from "react-router-dom";

import './index.css';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/lib/integration/react";
import configureStore from "./app/redux/store/store";

let { store, persistor } = configureStore();

ReactDOM.render(
    <BrowserRouter>
        <Provider store={store}>
            <PersistGate loading={null} persistor={persistor}>
                <App />
            </PersistGate>
        </Provider>,
    </BrowserRouter>, 
    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();
Enter fullscreen mode Exit fullscreen mode

HomeComponents.js from where you get the Users from the redux store and add a new User using dispatch

import React from 'react';
import { connect } from "react-redux";

import { AddUser } from "../../redux/actions/taskAction";

class Home extends React.Component {

    constructor(props) {
      super(props);
    }

  // creating new user using Redux-Thunk
    addNewUser = () => {
        let data = {
            "name": "muhammad awais",
            "job": "developer"
        }

        this.props.submit(
            data
        );
    }

    render() {

        // getting users from redux store
        console.log("this.props.Users : ",this.props.Users);

        return (
            <div>
                ...
            </div>
        );
    }
}

const mapStateToProps = state => ({
    Users: state.task.Users
});

const mapDispacthToProps = dispatch => {
    return {
        submit: (data) => {
            dispatch(AddUser(data))
        }
    };
};
// export default withStyles(styles)(ProductList);
export default connect(
    mapStateToProps,
    mapDispacthToProps
)(Home);
Enter fullscreen mode Exit fullscreen mode

That’s all. for now. I will release some new articles in detail.

Top comments (16)

Collapse
 
markerikson profile image
Mark Erikson

A few quick notes:

Collapse
 
muhammadawaisshaikh profile image
Muhammad Awais

I surely will do the next update to my repo with these recommended changes. great suggestions #mark.

Collapse
 
rajatpc profile image
rajatpc

I am here to get to know how to handle the errors, but bad luck you didn't cover up that part, I am hitting the API and got this object as an error in the http response->

error_code: 343
messsage: "Already accepted offer"
status: 0
Enter fullscreen mode Exit fullscreen mode

For normal response, I check everytime if status === 1 then that means I am having positive response, but in case of status === 0 that means some error coming from the server side but i am unable to print this message bcz it directly goes to the catch block and there i am getting simple http default error.
Do you have any idea how can I show this error message ?

Collapse
 
muhammadawaisshaikh profile image
Muhammad Awais

You have take that error from catch block and store in your component state and do render in the Error section, also you can use the Reusable toast service to show the error from catch block, so there are multiple solutions you have to manage in terms of your need. Thanks

Collapse
 
muhammadawaisshaikh profile image
Muhammad Awais

You missing something the boilerplate is tested and verified after each and every commit. So yeah

Collapse
 
fjordling profile image
Erika J

recommend you give redux-api-middleware a look, it will change your redux/api game, and allow you to move fully to fetch api (i.e. ditch axios) :)

Collapse
 
muhammadawaisshaikh profile image
Muhammad Awais

For sure. I will get hands on with it πŸš€

Collapse
 
rajatpc profile image
rajatpc

can you please look up my comment and suggest me any way out of it? That would be really appreciable. :)

Collapse
 
s0xzwasd profile image
John Doe

Great article and examples, thank you very much!

Collapse
 
muhammadawaisshaikh profile image
Muhammad Awais

pleasure man. it's all about sharing knowledge with community as you learn and grow. #bambooVibes

Collapse
 
s0xzwasd profile image
John Doe

Absolutely agree;)

Collapse
 
khalasnilesh profile image
khalasnilesh

getting axios is not defined error. plz suggest what is wrong

Collapse
 
muhammadawaisshaikh profile image
Muhammad Awais

hey, we use axios to make http request from our react/angular app, so i think this error is raised because you haven't install axios npm. try this command on your terminal/cmd of your project destination

C:/projects/react-redux-app> npm install axios

Collapse
 
falikyakov profile image
falikyakov

If you've already installed axios and are still getting this error,
you probably forgot to import - import axios from 'axios'

Collapse
 
eissorcercode99 profile image
The EisSorcer

I don't know if you've ever fixed this, but the Axios library has to be installed in order for this to work

Collapse
 
eissorcercode99 profile image
The EisSorcer

Has this structure changed since the release of React-Router v6?