1.0.4 • Published 1 year ago

typesafe-reducer v1.0.4

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

typesafe-reducer

A TypeScript library that helps create type-safe dispatcher and reducer functions

In order to use this library, you should have some familiarity with Typescript ADTs

Installation

Install the package:

npm install typesafe-reducer

Import it into your script:

import { generateReducer, State, Action } from 'typesafe-reducer';

Usage

// Let's imagine that our application can have two states
// (`MainState` and `LoadingState`). We would create an interface for each
// state, with a `type` property that is going to be used to
// discriminate between the states:
type LoadingState = State<'LoadingState', {
  readonly task: Promise<string[]>;
}>;
type MainState = State<'MainState', {
  readonly data: string[];
}>;

// And we should create a type called `states` that would combine all
// of the states our application has
type States =
  | LoadingState;
  | MainState;

// Let's do something similar for all the possible actions:
type LoadedAction = Action<'LoadedAction', {
  readonly data: string[];
}>;
type BeginLoadingAction = Action<'BeginLoadingAction'>;

type Actions =
  | LoadedAction
  | BeginLoadingAction;

// Now we can create the reducer:
// Notice how we supply state and action types in `<States, Actions>`
const reducer = generateReducer<States, Actions>({`
  'LoadedAction':({state, action})=>{
    return {
      type: 'MainState',
      data: action.data,
    };
  },
  'BeginLoadingAction':({state, action})=>{
    return {
      type: 'LoadingState',
      task: new Promise((resolve)=>
        setTimeout(()=>{
          resolve(['test','test','test']);
        },1000);
      );
    };
  }
});

// Now, you can use React's useReducer like this:
// https://github.com/specify/specify7/blob/90d80aae15ddbb588ea3fe556be3538db5e19483/specifyweb/frontend/js_src/lib/components/wbplanview.tsx#L84
// Or create your own dispatch:
const dispatch = (state:states,action:actions)=>{
  const newState = reducer(state,action);

  //handle setState here
  console.log(newState);
}

// After that, you can call the dispatch function from anywhere and
// provide the necessary arguments (`currentState` state is the
// current state of the application)
window.addEventListener('click', ()=>
  dispatch(
    currentState,
    {
      type: 'InitializeLoadingAction',
    }
  )
);

// Alternatively, you may want to mutate the current state inside of
// the reducer (though, be careful with it as side effects are
// dangerous)
let state:State1 = {
  type: 'State1',
  field: 'qwerty',
}
const dispatch = generateDispatch<actions>({
  'Action1':(action)=>{
    state = { // mutate the external state
      ...state,
      type: 'State2',
      is: true,
    }
    console.log(state,action);
  },
  'Action2':(action)=>{
    //mutate the external state here
  }
});

Advanced Usage

// There is a functionality to limit certain action calls only to some
// events. If action is called from a wrong event, an exception is
// triggered.
// To implement this, wrap reducer handler in an ensureState function,
// where first argument is an array of allowed types and second argument
// is the handler. Example:

const reducer = generateReducer<States, Actions>({`
  // Only allow calling LoadedAction from LoadingState
  'LoadedAction': ensureState(['LoadingState'],({state, action})=>{
    return {
      type: 'MainState',
      data: action.data,
    };
  }),
  'BeginLoadingAction': ensureState(['MainState'],({state, action})=>{
    return {
      type: 'LoadingState',
      task: new Promise((resolve)=>
        setTimeout(()=>{
          resolve(['test','test','test']);
        },1000);
      );
    };
  })
});

Credit

Some code was based on babakness/exhaustive-type-checking

1.0.4

1 year ago

1.0.3

2 years ago

1.0.2

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago