Reasync-hooks
A library to keep track of redux async action states, based on react-redux and react hooks.
Table of Contents
Installation
React Redux Async Hooks requires React 16.8.3 and React-redux 7.10 or later.
npm install --save reasync-hooks
This assumes that you’re using npm package manager with a module bundler like Webpack or Browserify to consume CommonJS modules.
Example
Basis example
You can play around with the following example in this codesandbox:
Step 1
Create store
;; const composeEnhancers = window__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;const asyncReduxMiddleware = ;const rootReducer = ;const store = ;
Step 2
Create async actions
; const FULFILLED_ACTION = "FULFILLED_ACTION";const REJECTED_ACTION = "REJECTED_ACTION";const asyncFulfilledAction = ;const asyncRejectedAction = ;
Step 3
Use hooks in your component
import useIsAsyncPendingSelector useOnAsyncFulfilled useOnAsyncRejected from "reasync-hooks";import useDispatch from "react-redux";import Button message from "antd"; const BasisExample = const dispatch = ; const isFulfilledActionPending = ; const isRejectedActionPending = ; //Notify something when async action is from pending to fulfilled ; //Notify something when async action is from pending to rejected ; return <div ="App"> <Button = = ="primary" > asyncFulfilledAction </Button> <Button = = ="danger" > asyncRejectedAction </Button> </div> ;;
Step 4
<Provider>
Nest the component inside of a import Provider from "react-redux"; const App = <Provider => <BasisExample /> </Provider>;;
Complete example:
import React from "react";import applyMiddleware combineReducers createStore compose from "redux";import asyncReduxMiddlewareCreator asyncStateReducer useIsAsyncPendingSelector useOnAsyncFulfilled useOnAsyncRejected asyncActionCreator from "reasync-hooks";import Provider useDispatch from "react-redux";import Button message from "antd";import"antd/dist/antd.css";import "./App.css"; /*Step 1: create store */const composeEnhancers = window__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;const asyncReduxMiddleware = ;const rootReducer = ;const store = ; /*Step 2: create async actions */const FULFILLED_ACTION = "FULFILLED_ACTION";const REJECTED_ACTION = "REJECTED_ACTION";const asyncFulfilledAction = ;const asyncRejectedAction = ; /*Step 3: use hooks in your component */const BasisExample = const dispatch = ; const isFulfilledActionPending = ; const isRejectedActionPending = ; //Notify something when async action is from pending to fulfilled ; //Notify something when async action is from pending to rejected ; return <div ="App"> <Button = = ="primary" > asyncFulfilledAction </Button> <Button = = ="danger" > asyncRejectedAction </Button> </div> ;; /*Step4: nest the component inside of a `<Provider>` */const App = <Provider => <BasisExample /> </Provider>;;
Advanced example: fetch data and handle error
There are two examples. One uses the reasync-hooks, the other only uses hooks. Both examples implement the same goal that fetch data and, if successful, notify the data, otherwise notify the error message.
1.Only use react hooks
Note: This example is only used to help users understand the usage of reasync-hooks and is not recommended.
import React useEffect useState from "react";import Buttonmessage from 'antd' const ExampleOnlyUseHooks = //Mock that fetch data successfully const fetchDataSuccess = { ; }; //Mock that an error occurs when fetch data const fetchDataError = { ; }; const isFulfilledAsyncPending setIsFulfilledAsyncPending = ; const data setSetData = ; const isRejectedAsyncPending setIsRejectedAsyncPending = ; const error setError = ; ; ; return <div ="App"> <Button = = ='primary' > asyncFulfilledAction </Button> <Button = = ='danger' > asyncRejectedAction </Button> </div> ;;
2.Use reasync-hooks
You can play around with the following example in this codesandbox:
Step 1
Customize the redux middleware
import asyncReduxMiddlewareCreator asyncStateReducer from "reasync-hooks"; const fulfilledHandler = resolveValue action dispatch ;;const rejectedHandler = rejectedReason action dispatch ;;const asyncReduxMiddleware = ;
Step 2
Create async actions
; const FULFILLED_ACTION = "FULFILLED_ACTION";const REJECTED_ACTION = "REJECTED_ACTION";//Mock that fetch data successfullyconst fetchDataSuccess = { ; };//Mock that an error occurs when fetch dataconst fetchDataError = { ; };const asyncFulfilledAction = ;const asyncRejectedAction = ;
Step 3
Create reducers
Note: To reduce boilerplate, you can use createReducer.
; const fulfilledReducer = { if actiontype === return ...state ...actiondata ; return state;};const errorReducer = { if actiontype === return ...state REJECTED_ACTION: actionerror ; return state;};
Step 4
Create store
;; const composeEnhancers = window__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;const rootReducer = ;const store = ;
Step 5
Use hooks in your component
import useIsAsyncPendingSelector useOnAsyncFulfilled useOnAsyncRejected from "reasync-hooks";import useDispatch useStore from "react-redux";import Button message from "antd"; const AdvancedExample = const dispatch = ; const store = ; const isFulfilledActionPending = ; const isRejectedActionPending = ; //Notify data when async action changes from pending to fulfilled ; //Notify error message when async action is from pending to rejected ; return <div ="App"> <Button = = ="primary" > asyncFulfilledAction </Button> <Button = = ="danger" > asyncRejectedAction </Button> </div> ;;
Step 6
<Provider>
Nest the component inside of a import Provider from "react-redux"; const App = <Provider => <AdvancedExample /> </Provider>;;
complete example:
import React from "react";import applyMiddleware combineReducers createStore compose from "redux";import fulfilledTypeCreator rejectedTypeCreator asyncReduxMiddlewareCreator asyncStateReducer useIsAsyncPendingSelector useOnAsyncFulfilled useOnAsyncRejected asyncActionCreator from "reasync-hooks";import Provider useDispatch useStore from "react-redux";import Button message from "antd";import"antd/dist/antd.css";import"./App.css"; /*Step 1: customize the redux middleware */const fulfilledHandler = resolveValue action dispatch ;;const rejectedHandler = rejectedReason action dispatch ;;const asyncReduxMiddleware = ; /*Step 2: create async actions*/const FULFILLED_ACTION = "FULFILLED_ACTION";const REJECTED_ACTION = "REJECTED_ACTION";//Mock that fetch data successfullyconst fetchDataSuccess = { ; };//Mock that an error occurs when fetch dataconst fetchDataError = { ; };const asyncFulfilledAction = ;const asyncRejectedAction = ; /*Step 3: create reducers */const fulfilledReducer = state = action if actiontype === return ...state ...actiondata ; return state;;const errorReducer = state = action if actiontype === return ...state REJECTED_ACTION: actionerror ; return state;; /* Step 4: create store */const composeEnhancers = window__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;const rootReducer = ;const store = ; /*Step 5: use hooks in your component */const AdvancedExample = const dispatch = ; const store = ; const isFulfilledActionPending = ; const isRejectedActionPending = ; //Notify data when async action changes from pending to fulfilled ; //Notify error message when async action is from pending to rejected ; return <div ="App"> <Button = = ="primary" > asyncFulfilledAction </Butto <Button = = ="danger" > asyncRejectedAction </Button> </div> ;; /*Step 6: nest the component inside of a `<Provider> */const App = <Provider => <AdvancedExample /> </Provider>;;
API
asyncActionCreator
const asyncAction =
Parameters
actionType:string
: An async action type.
asyncFunction:(getState)=>Promise
: A function that executes an asynchronous operation.
extraArgument?:any
: A custom argument that will be available by action.extraArgument
in the async action workflow.
Return
asyncAction:{types:[pendingType,fulfilledType,rejectedType],asyncFunction,extraArgument}
The reduxMiddleware
that is created by asyncReduxMiddlewareCreator
will only response the action
with a types
property . In fact, the idea behind react-redux-async-hooks
is that dispatch a corresponding action(pendingType, fulfilledType, rejectedType) when the Promise that asyncFunction
returns is in a different state(pending,fulfilled,rejected).
Note: asyncFunction
must be a function that returns a Promise.
asyncReduxMiddlewareCreator
const asyncReduxMiddleware =
Parameters
fullfilledHandler?:(resolveValue, action, dispatch, getState) => void
: If the promise has already been fulfilled, this handler will be called.
rejectedHandler?:( rejectedReason, action, dispatch, getState) => void
: If the promise has already been rejected, this handler will be called.
Note: Default handlers only call diapatch(action)
.
Return
void
Customize your asyncrReduxMiddeware
.
asyncStateReducer
A reducer that specifies how the application's state changes in response to async action to the store.
useIsAsyncPendingSelector
const isPending =
Parameters
actionTypes:string[]
: A group of async actions that are kept track of.
asyncStateReducerKey:string="asyncState"
: Under the hood, useIsAsyncPendingSelector
tries to get async action states by
//https://react-redux.js.org/api/hooks#useselector;
So you have to ensure asyncStateReducerKey
same with the key that is passed to combinReducers
for asyncSateReducer
.
Return
isAsyncPending:boolen
: True
means that at least one among asyncTypes
is in pending . False
means that all in asyncTypes
are in fulfilled
or rejected
.
useOnAsyncFulfilled
Parameters
actionTypes:string[]
: A group of async actions that are kept track of.
handler:(asyncType)=>void
: Run when any one of actionTypes
changes from pending to fulfilled. The asyncType
is passed to handler
is the one that triggers the handler
.
asyncStateReducerKey:string="asyncState"
: Same with this parameter in useIsAsyncPendingSelector
.
Return
void
useOnAsyncRejected
Parameters
actionTypes:string[]
: A group of async action types that are kept track of.
handler:(actionType)=>void
: Run when one of actionTypes
changes from pending to rejected. The actionType
is passed to handler
is the one that triggers the handler
.
asyncStateReducerKey="asyncState"
: Same with this parameter in useIsAsyncPendingSelector
.
Return
void
fulfilledTypeCreator
const fulfilledType =
Parameters
actionType:string
: An action type that represents an async action.
Return
asyncFulfilledType:string
: An async action type that you can use in your reducers to catch up the async action when it is in fulfilled.
rejectedTypeCreator
const rejectedType =
Parameters
actionType:string
: An action type that represents an async action.
Return
asyncFulfilledType:string
: An async action type that you can use in your reducers to catch up the async action when it is in rejected.
Todo
- Add test
License
[MIT]