UNPKG

use-reducer-async

Version:
132 lines (97 loc) 4.2 kB
# use-reducer-async [![CI](https://img.shields.io/github/workflow/status/dai-shi/use-reducer-async/CI)](https://github.com/dai-shi/use-reducer-async/actions?query=workflow%3ACI) [![npm](https://img.shields.io/npm/v/use-reducer-async)](https://www.npmjs.com/package/use-reducer-async) [![size](https://img.shields.io/bundlephobia/minzip/use-reducer-async)](https://bundlephobia.com/result?p=use-reducer-async) [![discord](https://img.shields.io/discord/627656437971288081)](https://discord.gg/MrQdmzd) React useReducer with async actions ## Introduction React useReducer doesn't support async actions natively. Unlike Redux, there's no middleware interface, but hooks are composable. This is a tiny library to extend useReducer's dispatch so that dispatching async actions invoke async functions. ## Install ```bash npm install use-reducer-async ``` ## Usage ```javascript import { useReducerAsync } from "use-reducer-async"; const initialState = { sleeping: false, }; const reducer = (state, action) => { switch (action.type) { case 'START_SLEEP': return { ...state, sleeping: true }; case 'END_SLEEP': return { ...state, sleeping: false }; default: throw new Error('no such action type'); } }; const asyncActionHandlers = { SLEEP: ({ dispatch }) => async (action) => { dispatch({ type: 'START_SLEEP' }); await new Promise(r => setTimeout(r, action.ms)); dispatch({ type: 'END_SLEEP' }); }, }; const Component = () => { const [state, dispatch] = useReducerAsync(reducer, initialState, asyncActionHandlers); return ( <div> <span>{state.sleeping ? 'Sleeping' : 'Idle'}</span> <button type="button" onClick={() => dispatch({ type: 'SLEEP', ms: 1000 })}>Click</button> </div> ); }; ``` ### Notes for abortability All async action handlers receive `signal` in the argument. Refer [`examples/04_abort/src`](./examples/04\_abort/src) for the usage. Note: The implementation depends on [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) in the DOM spec. If you are using an environment that doesn't have AbortController (for example IE11), you need a polyfill: [1](https://github.com/mo/abortcontroller-polyfill) [2](https://github.com/mysticatea/abort-controller) ## API <!-- Generated by documentation.js. Update this documentation by updating the source code. --> ### useReducerAsync useReducer with async actions #### Parameters * `reducer` **R** * `initialState` **ReducerState\<R>** * `asyncActionHandlers` **AsyncActionHandlers\<R, AsyncAction>** #### Examples ```javascript import { useReducerAsync } from 'use-reducer-async'; const asyncActionHandlers = { SLEEP: ({ dispatch, getState, signal }) => async (action) => { dispatch({ type: 'START_SLEEP' }); await new Promise(r => setTimeout(r, action.ms)); dispatch({ type: 'END_SLEEP' }); }, FETCH: ({ dispatch, getState, signal }) => async (action) => { dispatch({ type: 'START_FETCH' }); try { const response = await fetch(action.url); const data = await response.json(); dispatch({ type: 'FINISH_FETCH', data }); } catch (error) { dispatch({ type: 'ERROR_FETCH', error }); } }, }; const [state, dispatch] = useReducerAsync(reducer, initialState, asyncActionHandlers); ``` Returns **\[ReducerState\<R>, Dispatch\<ExportAction>]** ## Examples The [examples](examples) folder contains working examples. You can run one of them with ```bash PORT=8080 npm run examples:01_minimal ``` and open <http://localhost:8080> in your web browser. You can also try them in codesandbox.io: [01](https://codesandbox.io/s/github/dai-shi/use-reducer-async/tree/main/examples/01\_minimal) [02](https://codesandbox.io/s/github/dai-shi/use-reducer-async/tree/main/examples/02\_typescript) [03](https://codesandbox.io/s/github/dai-shi/use-reducer-async/tree/main/examples/03\_getstate) [04](https://codesandbox.io/s/github/dai-shi/use-reducer-async/tree/main/examples/04\_abort) ## Blogs * [How to Handle Async Actions for Global State With React Hooks and Context](https://blog.axlight.com/posts/how-to-handle-async-actions-for-global-state-with-react-hooks-and-context/)