In this post, we are going to explain how to send HTTP requests from React. We can send requests from every component but we are going to do that by centralizing the HTTP logic as some kind of a repository. For this type and size of the project, the centralized solution fits perfectly fine.
But if you have a larger and more complicated project it would be a good practice to split repository files, and thus, splitting the states. We are going to use axios as a third party library to send HTTP requests and Redux for centralizing the repository logic.
There is one important thing to note. We donât have to use Redux in React to create a central place for handling HTTP requests (this is just one way), we could create an additional file and export functions (that are going to handle HTTP requests) from that file. But Redux is quite common in the React projects, therefore we are going to explain in detail how Redux works.
So letâs start.
If you want to see all the basic instructions and complete navigation for the .NET Core series, check out the following link: Introduction to the .NET Core series.
For the complete navigation and all the basic instructions of the React series, check out: Introduction to the React series.
For the previous part check out: Navigation and Routing
The source code is available at GitHub: React series – react-series-part3-end branch
This post is divided into several sections:
- Creating the Axios Instance
- About Redux in React App
- Redux – Action Types
- Redux – Repository Actions (Action Container)
- React – Reducer
- Reducer File Registration
- Conclusion
Creating the Axios Instance
To install axios execute this command:
npm install --save axios

Even though we could use native axios instance (the one we have just installed) to send HTTP requests, the better way is to create our own instance. In this custom instance, we can define base url property and assign headers and other useful elements. If we need more instances of axios we can create them as well. So letâs create our custom axios instance.
Inside the src
folder, create a new folder and name it axios
. Inside that folder create a new file and name it axios.js
. Now let’s modify the axios.js
file:
import axios from 'axios'; const instance = axios.create({ baseURL: 'http://localhost:5000', headers: { headerType: 'example header type' } }); export default instance;
In this code example, we import the axios library and then create a new instance with the additional properties. This is very useful because we donât have to write full endpoints anymore (
axios.get(
http://locahost:5000/api/owner)
) for our HTTP requests. Now we can use relative paths (axios.get(/api/owner)
) because we are using our custom axios instance with the predefined baseURL property.
About Redux in React App
Redux is a state container for the JavaScript applications. Even though it seems a bit complicated at the beginning, with a bit of practice, you are going to realize that Redux is not that hard at all. And it helps a lot. Redux is not Reactâs library, it can fit with any other JavaScript framework, but it works well with React.
To install Redux execute this command:
npm install --save redux

The Redux on its own is not going to be enough. We need to create a relation between React and Redux and to accomplish that letâs install the react-redux
library:
npm install --save react-redux

Excellent.
Now we have both libraries required for our application to work with Redux. So, we can continue to the Redux implementation. But before we continue, let’s take a look at this diagram which closely explains how Redux works (Components part is a starting point):
Redux – Action Types
Letâs start by creating the following structure inside the src folder:
In the actions
folder, let’s create a new file and name it actionTypes.js
. Now let’s modify that file:
export const GET_DATA_SUCCESS = 'GET_DATA_SUCCESS'; export const POST_DATA_SUCCESS = 'POST_DATA_SUCCESS'; export const PUT_DATA_SUCCESS = 'PUT_DATA_SUCCESS'; export const DELETE_DATA_SUCCESS = 'DELETE_DATA_SUCCESS';
Our reducer file (which we are going to create later in this post) is going to use these action types to switch between different ways of updating the state.
Redux – Repository Actions (Action Container)
We need to create a new file in the actions
folder and name it repositoryActions.js
. We are going to handle HTTP async requests inside this file and return an object which reducer file is going to use to update the state. Every returned object must have at least one property with a name âtypeâ. The value of the type property is going to be one of the actionTypes
from the actionType.js
file (previously created).
Now let’s add the import statements for the actionTypes and for our axios instance in the repositoryActions.js
file:
import * as actionTypes from './actionTypes'; import axios from '../../axios/axios';
Then we need to add two functions. One to handle the GET request and the second one to return an object with the type property and the data from the server:
const getDataSuccess = (data) => { return { type: actionTypes.GET_DATA_SUCCESS, data: data } } export const getData = (url, props) => { return (dispatch) => { axios.get(url) .then(response => { dispatch(getDataSuccess(response.data)); }) .catch(error => { //TODO: handle the error when implemented }) } }
What are we doing in here?
We are exporting thegetData
 function. This function will be called from our component to fetch the data from the server (therefore we must export it from this file). Then with axios, we are sending the GET request. If it is successful we are dispatching the getDataSuccess
function which returns an object for the reducer file to use. This object has the mandatory type property and also the data property fetched from the server.
Below the getData
function, let’s implement all the other functions, by following the same pattern:
const postDataSuccess = (response) => { return { type: actionTypes.POST_DATA_SUCCESS, response: response } } export const postData = (url, obj, props) => { return (dispatch) => { axios.post(url, obj) .then(response => { dispatch(postDataSuccess(response)); }) .catch(error => { //TODO: handle the error when implemented }) } } const putDataSuccess = (response) => { return { type: actionTypes.PUT_DATA_SUCCESS, response: response } } export const putData = (url, obj, props) => { return (dispatch) => { axios.put(url, obj) .then(response => { dispatch(putDataSuccess(response)); }) .catch(error => { //TODO: handle the error when implemented }) } } const deleteDataSuccess = (response) => { return { type: actionTypes.DELETE_DATA_SUCCESS, response: response } } export const deleteData = (url, props) => { return (dispatch) => { axios.delete(url) .then(response => { dispatch(deleteDataSuccess(response)); }) .catch(error => { //TODO: handle the error when implemented }) } }
Thatâs it, we now have an implementation of the
repositoryActions.js
file and it’s a time to create and implement the reducer file.
React – Reducer
Let’s create a new file inside the reducers folder and name it repositoryReducer.js
:
In this file, we are going to check the type property which we return from the repositoryActions.js file. Then, based on the value of the type property, we are going to update our state.
So, let’s modify the repositoryActions.js
file:
import * as actionTypes from '../actions/actionTypes'; const initialState = { data: null, showSuccessModal: false }
We import the actionTypes and create the state with the name
initialState
. The data property is going to store the data from the server and the showSuccessModal
property serves to show or hide the success modal when a POST, PUT or DELETE action is successful.
Now letâs create a reducer function below our state object:
const reducer = (state = initialState, action) => { switch (action.type) { case actionTypes.GET_DATA_SUCCESS: return executeGetDataSuccess(state, action); case actionTypes.POST_DATA_SUCCESS: return executePostDataSuccess(state, action); case actionTypes.PUT_DATA_SUCCESS: return executePutDataSuccess(state, action); case actionTypes.DELETE_DATA_SUCCESS: return executeDeleteDataSuccess(state, action); default: return state; } } export default reducer;
This reducer function is accepting two parameters, the state which we initialize with our initial state and the action. We are going to use this state parameter to update our
initialState
and the action parameter to store the object (with at least type property) sent from therepositoryActions.js
file. So, whenever we dispatch any action (which returns an object with at least type property and all the other properties) from the repositoryActions.js
file, this reducer function is going to trigger and to accept the sent object inside the action parameter. As a consequence, the reducer is going to switch through the action types and to execute the corresponding function.
Finally, letâs add those corresponding functions right above our reducer
function:
const executeGetDataSuccess = (state, action) => { return { ...state, data: action.data } } const executePostDataSuccess = (state, action) => { return { ...state, showSuccessModal: true } } const executePutDataSuccess = (state, action) => { return { ...state, showSuccessModal: true } } const executeDeleteDataSuccess = (state, action) => { return { ...state, showSuccessModal: true } }
These functions are updating our state. First, we are deeply cloning our state object by using the spread (âŠ) operator and then just overriding the property we want to update in our state object. Because objects and arrays are reference types we need to execute a deep clone on them prior to any changes. That way we are updating the state immutably.
For this state to be available inside any owner component we need to register this reducer to the index.js
file.
Reducer File Registration
Before we register our reducer file we must install one more third-party library named thunk
:
npm install --save redux-thunk

This library enables us to send async requests with the Redux actions.
Now we can register our reducer:
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './containers/App'; import registerServiceWorker from './registerServiceWorker'; import 'bootstrap/dist/css/bootstrap.css'; import 'bootstrap/dist/css/bootstrap-theme.css'; import repositoryReducer from './store/reducers/repositoryReducer'; import { Provider } from 'react-redux'; import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; const store = createStore(repositoryReducer, applyMiddleware(thunk)); ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root')); registerServiceWorker();
In the code above, we import all the necessary files we need to register our reducer. Then we create the store and apply the middleware thunk and finally use the
Provider
component to provide our reducer to the React app.
There it is, we have prepared our redux setup for our project. The best part about it is that we can use it for any component inside our project and if we have components which require more complex or different kind of redux setup, all we have to do is to create another action and reducer file and to register it inside the index.js
file. In one of the next posts, we are going to show you how to accomplish that by combining reducers inside theindex.js
file.
Conclusion
Even though you may find a little bit hard to understand how Redux works, we believe that with this article and some practice you’ll get a handle on it. Bottom line is that Redux is not that hard, just as the opposite, it is pretty straightforward, after some practice.
By reading this post, you’ve learned:
- The way to set up the new Axios instance
- How to install Redux and React-Redux
- About Action Types, Action Containers and Reducers
- How to register reducer file
Thank you for reading the article and I hope you found something useful in it.
In the next part of the series, we are going to learn how to prepare the HTTP repository by using Axios and Redux.
Really great article series! I went through the .Net Core, now through React and learned a lot. One thing that could be improved in the React article is to put the production configuration, because there is different baseUrl when deploying. You describe that in the Angular series (environment.ts vs environment.prod.ts), but here it is missing so when I deployed the client in wwwroot, the calls to backend had wrong url.
Hello noro, thank you very much for your suggestion, you are completely right. There are few more things that we need to upgrade in the React series (like new life cycle and hooks), but just can’t find time for that. But, we have it in our plans. All the best.
Hello Marinko,
I am following your series most of the time and I really like the concept you are solving the problems. Very nice tutorials worth of reading and following.
I have a question about redux store because I am new to the concept of react and redux. I have a form where user can fill required data and register a new user. Also I have 2 dropdown lists to populate from an api. One contains userroles and the other on workroles. I have set up the redux like you did it here, with few small changes but the logic is the same. When I use mapDispatchToProps and return 2 different api calls like this(I call those methods in componentdidmount):
const mapDispatchToProps = dispatch => {
return {
getUserRoles: (url, props) =>
dispatch(repositoryActions.getData(url, props)),
getWorkRoles: (url, props) =>
dispatch(repositoryActions.getData(url, props))
};
};
than mapstatetoprops :
const mapStateToProps = state => {
return {
data: state.repository.data
};
};
I got the data displayed in dropdown but the last fetched data overwrites the first from first api call. It is obvious why that is happening because I have only one property data in mapstatetoprops and in reducerrepository. How can I without making more logic in redux and adding one fetch for userroles and other one for workroles with this “generic” let’s say method getData() get both api data and render them each in separate dropdownlists? So one getData() from redux, 2 api calls in component for 2 dropdownlists. I have tried create state and save every call data but I can’t because data is comming from mapstatetoprops and there I only have one data from repository? I hope you understand what I am trying to achieve.
Hello Ivan. I completely understund what you are asking, but my answer must be to separate those calls. It is cleaner and better solution. In my example I have only one data and one store, because I work with a single entity, and the point is to show how things could be done and not to work for every app đ So, I know that this is not what you have expected to get from my answer, but I would separete those in my project for sure. I would even create a different repo file for every entity as well. Best regards.
Hi Marinko,
Can you please explain where registerServiceWorker.js comes from – it seems to suddenly appear from thin air.
Thanks, Matt
Hello Matt. In the React version which I used for this tutorial, this file “registerServiceWorker.js” came with the create template. So this is just an import statement for it. Basically, it has been there since the beginning of this tutorial đ
There seems to be two files named similarly in this tut: “repositoryAction.js” and “repositoryActions.js”, is this correct? Also, it seems to mention an “actions.js” when in the context of discussing the “repositoryActions.js” file. (page is https://code-maze.com/react-net-core-http-axios-redux/)
Hello Douglas. It’s all repositoryActions. Just some typos. I will fix that right away. Thanks for suggesting it.
There is another issue…tut mentions making folder “Axios” and file “Axios.js” in it. The source code in the tut has “import axios from ‘../../axios/aixos’;” which is incorrect — see the file referenced is ‘aixos’ when it should be ‘axios’. The code at in the gitbub repo does have this filename “aixos.js” : https://github.com/CodeMazeBlog/react-series/tree/react-series-part4-end/accountownerclient/src/axios
Would be swell if you could make these corrections to save the next person the time I spent.
Will be fixed. It is very strange that we didn’t see that. Again typo in the file name.