Redux reducer and action builders with type-safety. TypeScript definitions are included.

Compared to Others

Tiny - Less than 1kB minified and GZipped (according to BundlePhobia).

- Less than 1kB minified and GZipped (according to BundlePhobia). Minimal - No dependencies, no bells, no whistles.

- No dependencies, no bells, no whistles. Simple - Very small API, no magic, and standard Redux patterns.

- Very small API, no magic, and standard Redux patterns. Un-opinionated - Just as flexible as a vanilla reducer and handmade actions. Do or don't use flux standard actions, use any Redux middleware, layout your Redux file structure any way you like, etc.

- Just as flexible as a vanilla reducer and handmade actions. Do or don't use flux standard actions, use any Redux middleware, layout your Redux file structure any way you like, etc. Type-safe - Typed actions and action objects.

- Typed actions and action objects. Extensible - Inject middleware at the reducer scope, and add reducer cases lazily.

Getting Started

Install with yarn :

yarn add @xornot/redux-case

Install with npm :

npm add @xornot/redux-case

First, create some actions.

import { createAction } from " @xornot/redux-case " ; ; ;

Note: These "actions" are factory functions which use the callback to create an action object.

Next, create a reducer with some cases to handle your actions.

import { createReducer } from " @xornot/redux-case " ; import { set , increment } from " ./actions " ; . case ( set , ) . case ( increment , ) ;

Finally, use the reducer like any other Redux reducer.

import { createStore } from " redux " ; import { counter } from " ./counter " ; import { set , increment } from " ./actions " ; ; store . dispatch ( set ( 5 ) ) ; store . dispatch ( increment ( ) ) ;

Try The Demo

A more complete demo project is included. Run the following commands to clone this repository and start a calculator web application locally.

git clone https://github.com/xornot-io/redux-case.git cd redux-case yarn install yarn start

Initial State Factory

You can alternatively provide a function which returns the initial state object. The function will be called lazily the first time the reducer is called, if the initial state is undefined. It may never be called if a defined initial state is provided when calling createStore .

;

Reducer Middleware

You can provide one or more reducer middleware functions after the initial state parameter. These middleware functions can modify or log the state before and after the action reducer receives it.

Middleware is only invoked when a case is matched. Not every time the reducer is invoked.

Middleware functions should have the following prototype.

S ;

Here's an example of using immer to replaced the state with a "draft" that can be safely modified in-place. We don't even have to return the modified state. Immer will see that the state was modified and handle creating a new state object with correctly enforced immutability.

import immer from " immer " ; import { createReducer } from " @xornot/redux-case " ; import { set , increment } from " ./actions " ; . case ( set , ) ; . case ( increment , ) ;

Lazy Action Definition

The reducer.case() method can be used at any time, even after the reducer has been used to create a store! This allows actions to be lazily defined.

Case For Multiple Actions

You can pass an array of actions to the .case() method to handle any of the actions with the same case callback. The action argument type will be a union of the action object types of the actions.

; ; ;

Inferring State and Action Types

Some TypeScript helper types are included which will allow you to infer the state type from a reducer or store, and the action type from an action creator.

import { InferState , InferAction } from " @xornot/redux-case " ; type ReducerStateType = InferState < typeof reducer > ; type StoreStateType = InferState < typeof store > ; type ActionType = InferAction < typeof action > ;

Classic Reducers

You can still have action type-safety using createAction , without using the createReducer utility. Actions created using the createAction utility have a .match(action: AnyAction): boolean method that works as a type guard.