Détail du package

use-reducer-async

dai-shi62.3kMIT2.1.2

React useReducer with async actions

react, reducer, async

readme

use-reducer-async

CI npm size discord

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

npm install use-reducer-async

Usage


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 for the usage.

Note: The implementation depends on AbortController in the DOM spec. If you are using an environment that doesn't have AbortController (for example IE11), you need a polyfill: 1 2

API

useReducerAsync

useReducer with async actions

Parameters

  • reducer R
  • initialState ReducerState\<R>
  • asyncActionHandlers AsyncActionHandlers\<R, AsyncAction>

Examples

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 folder contains working examples. You can run one of them with

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 02 03 04

Blogs

changelog

Change Log

[Unreleased]

[2.1.2] - 2025-07-21

Changed

  • Do not import ReducerAction type #47

[2.1.1] - 2022-05-24

Changed

  • fix build for UMD
  • fix: useAbortSignal to work with React 18 #35

[2.1.0] - 2021-12-23

Changed

  • feat: support recursive dispatching async actions #26

[2.0.2] - 2021-11-04

Changed

  • Modern build
  • Fix package.json properly for ESM

[2.0.1] - 2020-04-04

Changed

  • Avoid initializing AbortController repeatedly

[2.0.0] - 2020-02-29

Changed

  • Improve isClient detection for useIsomorphicLayoutEffect
  • Support abortability
  • New API for async action handlers [BREAKING CHANGE]

[1.0.0] - 2020-02-26

Changed

  • Update only README

[0.6.0] - 2020-01-18

Changed

  • Make ExportAction type omittable

[0.5.0] - 2019-11-01

Changed

  • Support getState
  • Improve typings (breaking change)

[0.4.0] - 2019-10-29

Changed

  • Improve and simplity typing

[0.3.0] - 2019-10-24

Changed

  • Better typing and better type inference in examples

[0.2.0] - 2019-10-22

Changed

  • Better typing and checking type property existence

[0.1.0] - 2019-10-22

Added

  • Initial release