npm install --save redux-thunk-status
-
First you'll need to ensure
redux-thunk
middleware is applied to store already:import { createStore, applyMiddleware } from 'redux' import thunkMiddleware from 'redux-thunk' const store = createStore( reducer, applyMiddleware( thunkMiddleware ) )
-
Install the reducer into the
async
path of yourcombineReducers
:import { combineReducers } from 'redux' import { reducer as asyncReducer } from 'redux-thunk-status' const reducer = combineReducers({ async: asyncReducer, ...reducers })
-
Create asynchronous actions/thunks (These must return a promise):
// Thunk returning promise const getDataFromPromise = () => { return ( dispatch, getState ) => { return new Promise(resolve => { setTimeout(() => { resolve(['item1', 'item2', 'item3']) }, 1000) }) } } // or with ajax import async from 'async' const getDataFromAjaxCall = () => { return ( dispatch, getState ) => { return axios.get( '/api/admin/users' ) .then(response => { // dispatch more actions // optionally return explicit resolution return Promise.resolve(response) }) .catch(error => { // dispatch more actions // optionally return explicit rejection return Promise.reject(error) }) }) } }
-
Name and wrap dispatchable actions with the
captureStatus( name, action )
method:const mapDispatchToProps = ( dispatch ) => { return { getItems: () => { dispatch( captureStatus( 'get_items', getDataFromAjaxCall() ) ) } } }
-
Get status of thunk with the
getLoadingStatus( name )( state )
selector method:const mapStateToProps = ( state ) => { return { loading: getLoadingStatus( 'get_items' )( state ) } }
-
Optionally use the
AwaitLoading
component to conditionally render jsx blocks or components:import React from 'react' import { connect } from 'react-redux' import { captureStatus, getLoadingStatus, AwaitLoading } from 'redux-thunk-status' // Thunk returning promise const getDataFromPromise = () => { return ( dispatch, getState ) => { return new Promise(resolve => { setTimeout(() => { // dispatch your action creator to save to store dispatch( setItemsInReduxStoreAction(['item1', 'item2', 'item3']) resolve(true) }, 1000) }) } } class Items extends React.Component { constructor( props ){ super( props ) } componentDidMount(){ this.props.getItems() } render(){ const { items, loading } = this.props return ( <div> <AwaitLoading loading={loading}> <ul> {items.map(item => ( <li>{item}</li> )} </ul> </AwaitLoading> </div> ) } } const mapStateToProps = ( state ) => { return { items: state.items, loading: getLoadingStatus( 'get_items' )( state ) } } const mapDispatchToProps = ( dispatch ) => { return { getItems: () => { dispatch( captureStatus( 'get_items', getDataFromPromise() ) ) } } } export default connect( mapStateToProps, mapDispatchToProps )( Items )
You can also pass a custom loading component to the
component
prop ofAwaitLoading
:const RenderLoading = ( props ) => { return ( <div> <i className="fa fa-refresh fa-spin fa-3x fa-fw"></i> <span className="sr-only">Loading...</span> </div> ) } <AwaitLoading loading={loading} component={RenderLoading}> {/* child jsx */} </AwaitLoading>