How to use async methods inside Redux actions using Redux-Thunk
If you are using Redux, probably you noticed that actions only can return plain objects, something like:
{ type: HERE_THE_ACTION_TYPE, anObject }
If we try to return any other thing like for example a function, the browser will nicely remember it with a message:
In the message, Redux is telling us the solution: Use custom middleware for async actions
This article will help us to prevent the error using Redux-Thunk.
First steps
We are going to do an app from scratch so first steps are related to setup the app and add the required packages.
npx create-react-app demo-react-thunk
cd demo-react-thunk
yarn add redux react-redux redux-thunk
The good stuff
Now we are going to create a component that will list some users. Since we want to show how to use Redux-Thunk we will need:
- An action: it will fetch the user data
- A reducer: to put the data in the global state
- An store: to save the global state
- A component: to show the data
The action
Lets create the file src/actions/user.js
with the following content:
export const fetchUsers = () => {
return dispatch => {
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(json => dispatch({ type: 'FETCH_ALL_USERS', json }))
}
}
What are we doing there?
Instead of returning a plain object we are returning a function that expects the dispatch
method as parameter, the function is asking for some dummy data using https://jsonplaceholder.typicode.com/ service, transform the response to json and then return the plain object action using dispatch
. For details about dispatch method, please read the official redux docs
The reducer
This is a common reducer, I mean, we don't need to change anything to support async actions. Lets create the file src/reducers/user.js
with the following content:
import { combineReducers } from 'redux';const user = (state = { users: [] }, action) => {
switch (action.type) {
case 'FETCH_ALL_USERS':
return {
...state,
users: action.json
};
default:
return state;
}
};export default combineReducers({
user
});
The store
Here is where magic happens, in the file src/index.js
we should import the following:
import { applyMiddleware, createStore } from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from './reducers/user';
and then we should create the store with following code:
const store = createStore(
rootReducer,
applyMiddleware(
thunkMiddleware
)
);
What we are doing here, is asking our store to apply a middleware, remember the original message about using custom middleware for async actions?. The middleware in this case is Redux-Thunk that will help us to run async methods in the action.
The code for src/index.js
should looks like the following
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import { applyMiddleware, createStore } from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from './reducers/user';const store = createStore(
rootReducer,
applyMiddleware(
thunkMiddleware
)
);ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
The component
Finally, we need a component to show the users fetched by redux action, we are going to usesrc/App.js
to show the data, it will looks like the following code:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import { connect } from "react-redux";
import { fetchUsers } from "./actions/user"class App extends Component {
componentDidMount() {
this.props.fetchUsers();
}render() {
const { users } = this.props;
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Redux Thunk Demo</h1>
</header>
<section>
{
users.length > 0 &&
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Phone</th>
</tr>
</thead>
<tbody>
{
users.map(user =>
<tr key={user.id}>
<td>{user.name}</td>
<td>{user.email}</td>
<td>{user.phone}</td>
</tr>
)
}
</tbody>
</table>
}
</section>
</div>
);
}
};const mapStateToProps = (state) => {
return state.user;
}const mapDispatchToProps = (dispatch) => ({
fetchUsers: () => dispatch(fetchUsers())
});export default connect(mapStateToProps, mapDispatchToProps)(App);
That's all folks
Now we can run the app and see how it shows some user data using async actions in redux.
You can see the complete code in the following repository:
If you like this story clap as many times as you want, and to see similar stories about technology, check our publications and leave us a comment if you have any question.
If you need a team that can help you to implement your ideas in React JS or React Native, feel free to ping us using our website and filling the contact form.
Gustavo
Mail: gustavo@alturasoluciones.com
Twitter: @Aguardientico
LinkedIn: www.linkedin.com/in/gustavoagonzalez
Blog in spanish: http://aguardientech.blogspot.com