Example Idea Redux thunk is needed for asynchronous calls, such as data fetching. Thunk middleware allows for an action creator function to return a function instead of an action object. Redux-thunk does is a middleware that looks at every action that passes through the system, and if it’s a function, it calls that function. Great explanation what the thunk is. Basic 'action creator' function.
function userLoggedIn() {
return {
type: 'USER_LOGGED_IN',
username: 'dave'
};
}
// use it with dispatch
dispatch(userLoggedOut())
Thunk version
function logOutUser() {
return function(dispatch, getState) {
return axios.post('/logout').then(function() {
// pretend we declared an action creator
// called 'userLoggedOut', and now we can dispatch it
dispatch(userLoggedOut());
})
}
}
// use it with dispatch
dispatch(logOutUser()).then(() => {
// do something after logout
})
Thunks can dispatch new actions if they need to and access the current state. Installation npm install redux-thunk Reducer
const initialState = { loading: false, users: [], err: '' }
const users = (state = initialState, action) => {
switch (action.type) {
case 'FETCH_USERS_REQUEST':
return { ...state, loading: true }
case 'FETCH_USERS_SUCCESS':
return { loading: false, users: action.payload, err: '' }
case 'FETCH_USERS_FAILURE':
return { loading: false, users: [], err: action.payload }
default: return state
}
}
const rootReducer = combineReducers({
users
})
Action creators
const fetchUsersRequest = () => ({ type: 'FETCH_USERS_REQUEST' })
const fetchUsersSuccess = (users) => ({ type: 'FETCH_USERS_SUCCESS', payload: users })
const fetchUsersFailure = (err) => ({ type: 'FETCH_USERS_FAILURE', payload: err })
const fetchUsers = () => {
return (dispatch) => {
dispatch(fetchUsersRequest())
axios.get('https://jsonplaceholder.typicode.com/users')
.then(sleeper(1000))
.then(res => dispatch(fetchUsersSuccess(res.data)))
.catch(err => dispatch(fetchUsersFailure(err.message)))
}
}
Store
const middlewares = [thunk]
const store = createStore(
rootReducer,
composeWithDevTools(
applyMiddleware(...middlewares)
)
)
Component
const style = { border: '2px solid grey', padding: '10px', margin: '10px', maxWidth: '500px' }
function Component() {
const users = useSelector(state => state.users)
const dispatch = useDispatch()
return (
<div style={style}>
<button onClick={() => dispatch(fetchUsers())}>Fetch users</button><br />
<div>
{users.loading && 'Loading...'}
{users.err && users.err}
{!users.loading && !!users.users.length && users.users.map(user => <div key={user.id}>{user.name}</div>)}
</div>
</div>
)
}
Whole code
import { combineReducers, createStore, applyMiddleware } from 'redux'
import { Provider, useSelector, useDispatch } from 'react-redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunk from 'redux-thunk'
import axios from 'axios'
import sleeper from '/functions/sleeper'
// #region REDUCERS (sets and changes state)
const initialState = { loading: false, users: [], err: '' }
const users = (state = initialState, action) => {
switch (action.type) {
case 'FETCH_USERS_REQUEST':
return { ...state, loading: true }
case 'FETCH_USERS_SUCCESS':
return { loading: false, users: action.payload, err: '' }
case 'FETCH_USERS_FAILURE':
return { loading: false, users: [], err: action.payload }
default: return state
}
}
const rootReducer = combineReducers({
users
})
// #endregion
// #region ACTION CREATORS
const fetchUsersRequest = () => ({ type: 'FETCH_USERS_REQUEST' })
const fetchUsersSuccess = (users) => ({ type: 'FETCH_USERS_SUCCESS', payload: users })
const fetchUsersFailure = (err) => ({ type: 'FETCH_USERS_FAILURE', payload: err })
const fetchUsers = () => {
return (dispatch) => {
dispatch(fetchUsersRequest())
axios.get('https://jsonplaceholder.typicode.com/users')
.then(sleeper(1000))
.then(res => dispatch(fetchUsersSuccess(res.data)))
.catch(err => dispatch(fetchUsersFailure(err.message)))
}
}
// #endregion
// #region STORE
const middlewares = [thunk]
const store = createStore(
rootReducer,
composeWithDevTools(
applyMiddleware(...middlewares)
)
)
// #endregion
// #region Component
const style = { border: '2px solid grey', padding: '10px', margin: '10px', maxWidth: '500px' }
function Component() {
const users = useSelector(state => state.users)
const dispatch = useDispatch()
return (
<div style={style}>
<button onClick={() => dispatch(fetchUsers())}>Fetch users</button><br />
<div>
{users.loading && 'Loading...'}
{users.err && users.err}
{!users.loading && !!users.users.length && users.users.map(user => <div key={user.id}>{user.name}</div>)}
</div>
</div>
)
}
// #endregion