Synchronous Actions
- Till now, as soon as an action was dispatched, the state was immediately updated.
- If you dispatch the BUY_CAKE action, the nomOfCakes was right away decremented by 1.
Async Actions
- Asynchronous API calls to fetch data from an end point and use that data in your application.
Goals for our application
- Fetches a list of users from an API end point and stores it in the redux store.
State
Typically with data fetching, there are 3 properties for the state object - loading flag, data, error message.
state = {
loading: true,
users: [],
error: ''
}
- loading : Normally used for loading spinner in a component
- data : List of users
- error message : Display error to the user
Actions
- FETCH_USERS_REQUEST : Fetch list of users
- FETCH_USERS_SUCCESS : Fetched successfully
- FETCH_USERS_FAILURE : Error fetching the data
Reducers
case: FETCH_USERS_REQUEST
loading: true
case: FETCH_USERS_SUCCESS
loading: false
users: data( from API )
case: FETCH_USERS_FAILURE
loading: false
error: error( from API )
Async action creators
There are two packages to install for the implemetation of making an API call working with redux.
- axios : Requests to an API end point
- redux-thunk : Define async actino creators. Middleware that we will apply.
Implementation
1. Create a new file named 'asyncActions.js'
2. Create an initial state, an object with three properties.
We have to define 3 things - state, actions, reducer.
const initialState = {
loading: false,
users: [],
error: ''
}
3. Declare the constants for the anction types.
const FETCH_USERS_REQUEST = 'FETCH_USERS_REQUEST';
const FETCH_USERS_SUCCESS = 'FETCH_USERS_SUCCESS';
const FETCH_USERS_FAILURE = 'FETCH_USERS_FAILURE';
4. Create action creators.
Action creators are functions that return an action.
//actions creators
const fetchUsersRequest = () => {
return {
type: FETCH_USERS_REQUEST
}
}
const fetchUsersSuccess = users => {
return {
type: FETCH_USERS_SUCCESS,
payload: users
}
}
const fetchUsersFailure = error => {
return {
type: FETCH_USERS_FAILURE,
payload: error
}
}
5. Define the reducer function.
Based on the action type, we need to return a new state.
- REQUEST : return an object that has a copy of the existing state, and only set the loading flag to true.
- SUCCESS : return an object where loading is false, 'users' is 'action.payload', property that we send through the action creator. Also, clear out 'error' message in case there was any.
- FAIL : loading is done so set it false, users will be an empty array, error is 'action.payload'.
//reducer
const reducer = (state = initialState, action) => {
switch(action.type){
case FETCH_USERS_REQUEST:
return {
...state,
loading: true
}
case FETCH_USERS_SUCCESS:
return {
loading: false,
users: action.payload,
error: ''
}
case FETCH_USERS_FAILURE:
return {
laoding: false,
users: [],
error: action.payload
}
}
}
The arrow function gets two argumnets - state with a default value of 'initialState', and the second argument is an 'action' based on the action type.
'action.payload' in reducer corresponds to the payload property that the action contains(look point 4).
If the request was succesful, the payload is the array of users, and if it was a failure the payload will be an error message.
6. On the top, add these codes to create redux store.
const redux = require('redux');
const createStore = redux.createStore;
7. At the bottom, add this code to create a store with reducer.
const store = createStore(reducer);
8. Install the two packages by executing the following code in your terminal.
npm install axios redux-thunk
Now we have to apply the redux-thunk middleware to the redux store.
9. At the top add the following code to get hold of the applyMiddleware function from redux.
const redux = require('redux');
const createStore = redux.createStore;
const applyMiddleware = redux.applyMiddleware; //add this code
10. In the createStore method, pass it as a second argument after importing thunkMiddleware.
const redux = require('redux');
const thunkMiddleWare = require('redux-thunk').default; //add this line
const createStore = redux.createStore;
const applyMiddleware = redux.applyMiddleware;
/*
action creator, reducer, ...
*/
const store = createStore(reducer, applyMiddleware(thunkMiddleWare)); //modify code
11. Import axios by adding the following line at the top.
const redux = require('redux');
const thunkMiddleWare = require('redux-thunk').default;
const axios = require('axios'); //add this line
Our final step is to define the async action creator.
12. Define a function called 'fetchUsers'.
This is an action creator, and action creator returns an action.
//action creator, must return an action
// but thunk makes it able to return a function
const fetchUsers = () => {
return function(dispatch) {
axios.get('http://jsonplaceholder.typicode.com/users')
.then(response => {
//response.data is array of users
const users = response.data.map(user => user.id);
})
.catch(error => {
//error.message is the error description
})
}
}
- The thunk middlware makes the action creator possible to 'return a function' of an action object.
- So, we can return a fucntion, and this function doens't have to be pure, so it is allowed to have side effects such as async API calls.
- This function can also dispatch actions - This is possible because it receives the dispatch method as its argument.
http://jsonplaceholder.typicode.com/users
This JSONPlaceholder url returns 10 array of users.
13. Dispatch appropriate actions before calling the API call.
const fetchUsers = () => {
return function(dispatch) {
dispatch(fetchUsersRequest); // add this line
axios.get('http://jsonplaceholder.typicode.com/users')
.then(response => {
const users = response.data.map(user => user.id);
dispatch(fetchUsersSuccess(users)) // add this line
})
.catch(error => {
dispatch(fetchUsersFailure(error.message)); // add this line
})
}
}
Dispatching 'fetchUsersRequest' before the API call, 'loading' is set true.
Dispatching 'fetchUsersSucces' with users (== response.data) will store the user in the state. To return only the ID for each of the users, map user.id
Dispatch 'fetchUsersFailure' passing in 'error.message'.
14. At the bottom, subscribe to the store and dispatch this asynchronous action creator.
const store = createStore(reducer, applyMiddleware(thunkMiddleWare));
store.subscribe(() => { console.log(store.getState())}); // add this line
store.dispatch(fetchUsers()); // add this line
15. Execute the file with the code in terminal :
node asyncActions.js
Results
16. If we change the url to an invalid one like below, the results are shown like this:
axios.get('http://jsonnnnnnn.typicode.com/users')
Summary
1. Import the redux thunk middleware
2. Pass it to the createStore function
3. It allows the action creator to return a function instead of an action.
4. The funcion can perform side effects like asynchronous tasks. It can also dispatch regular actions which will be handled by reducer.
This posting is a summary of the Youtube video 'React Redux Tutorial' by Codevolution.
Github repository - https://github.com/rocher71/redux-demo
This commit - https://github.com/rocher71/redux-demo/commit/319b3aa49afcc26cc4ff98c3b1268384d13bf4fc