Rainbow actions 🌈

This is not a project to reduce redux boilerplate. The project goal is to gather the same domain actions and action creators in namespaces by their domain purpose.
Install
npm install rainbow-actions
Basic usage
import {createActions} from 'rainbow-actions'
type PostId = string
type PayloadDictionary = {
init: PostId;
fulfill: {id: PostId; title: string};
error: {code: number};
fail: never;
}
const requestPost = createActions<PayloadDictionary>()('get_post')
const initType = requestPost.init.type
const initAction = requestPost.init('42')
Typescript raises an error when one attempts to access a property of requestPost any other than the keys of PayloadDictionary.
import {createActions, ExtractNamespaceActions, ExtractManyNamespaceActions} from 'rainbow-actions'
type RequestPayloads = {
init: number;
get: string;
end: never;
}
const request = createActions<RequestPayloads>()('request')
type Actions = ExtractNamespaceActions<typeof request>
Actions
type AllActions = ExtractManyNamespaceActions<[typeof request, typeof anotherNamespace ]>
Advanced usage
Note the object passed to the function after the action type base:
import {createActions} from 'rainbow-actions'
type PayloadDictionary = {
one: number;
two: boolean;
three: string;
}
const base = createActions<PayloadDictionary>()('base', {
one: {payload: (id) => id * 2},
two: {meta: (flag) => `flag is ${flag}`},
three: {error: true},
})
const one = base.one(5)
const two = base.two(true)
const three = base.three('hey')
Caveat
Both basic and advanced usage work using the Proxy object. This means if you are not 100% confident about your types, then developers may accidentally dispatch incorrect actions since any method on the created action namespace will be a valid action creator. If this sounds scary, worry no more, this package also provides a safe version.
Safe usage
If you prefer to avoid the Proxy usage or are not 100% confident in the typescript types in your project, you can use the safe version. It requires to pass all of the actions for the namespace to be accessible.
import {createActions} from 'rainbow-actions/safe'
type PayloadDictionary = {
none: never;
one: number;
two: boolean;
three: string;
}
const base = createActions<PayloadDictionary>()('base', {
none: 0,
one: {payload: (id) => id * 2},
two: {meta: (flag) => `flag is ${flag}`},
three: {error: true},
})
const none = base.none()
const one = base.one(5)
const two = base.two(true)
const three = base.three('hey')
Reducers
There are two ways one may want to write reducers redux-actions like or immer way
Redux actions like reducers
import {handleActions} from 'rainbow-actions/reducer'
import {request} from './actions'
import type {Actions} from './actions'
import type {State} from './state'
const defaultState = {}
export const reducer = handleActions<State, Actions>(
{
[request.init.type]: (state, action) => ({
...state,
count: action.payload,
isLoading: true,
})
},
defaultState,
)
Immer reducers
On the other hand you can use immer. Immer is a peer dependency and you have to have it installed in your project.
import {handleActions} from 'rainbow-actions/immer'
import {request} from './actions'
import type {Actions} from './actions'
import type {State} from './state'
const defaultState = {}
export const reducer = handleActions<State, Actions>(
{
[request.init.type]: (state, action) => {
state.count = action.payload
state.isLoading = true
}
},
defaultState,
)
Acknowledgments
This package is highly inspired by typed-actions, piler by @lttb, and immer by @mweststrate.