@featherweight/actions-reducer
TypeScript icon, indicating that this package has built-in type declarations

0.4.1 • Public • Published

Featherweight actions reducer

Setup

npm install --save @featherweight/actions-reducer

Usage

import {createAction, createReducer} from '@featherweight/actions-reducer'

/* Define your actions */
const increment = createAction('counter: increment')
const add = createAction<number>('counter: add')

/* Create reducer */
const initialCounter = 0
const reducer = createReducer(
  on => [
    on(increment, state => state + 1),
    on(add, ((state, action) = state + action.payload)),
  ],
  initialCounter,
)

/* All set, you can use reducer */
const reduxStore = createStore(reducer)

/* Also you can define your handlers outside of reducer using HandlerOf type */
const decrement = createAction('counter: decrement')
const decrementHandler: HandlerOf<
  typeof initialState,
  typeof decrement
> = state => state - 1

/* And you can attach handlers to your reducer dynamically */
reducer.on(decrement, decrementHandler)

/* It supports meta and error fields as well */
const allInOne = createAction<string | Error, {flag: boolean}>('all in one')
allInOne(new Error('nope'), {flag: true})

API

action

Flux Standard Action compatible action.

<P, M>(type: ActionType, payload: P, meta: M) => FSA<P, M>
import {action} from '@featherweight/actions-reducer'

action('plain action')
// {type: 'plain action'}

action('with payload', {ping: 'pong'})
// {type: 'with payload', payload: {ping: 'pong'}}

action('with meta', 42, {hi: 'there'})
// {type: 'with payload', payload: 42, meta: {hi: 'there'}}

createAction

Action creator factory.

<P, M>(type: ActionType) => (payload?: P, meta?: M) => FSA<P, M>
import {createAction} from '@featherweight/actions-reducer'

const ping = createAction('ping')
ping()
// {type: 'ping'}

const setValue = createAction<number>('set value')
setValue(300)
// {type: 'set value', payload: 300}

const fetchUser = createAction<string, {ts: Date}>('fetch user')
fetchUser('user-id', {ts: new Date()})
// {type: 'fetch user', payload: 'user-id', meta: {ts: Date}}

createReducer

Reducer creator with type inference.

<S>(createHandlers: (on: Handler<S>) => HandlerTuple<S>[], initialState?: S) => Reducer
import {createAction, createReducer} from '@featherweight/actions-reducer'

const actions = {
  ping: createAction('ping'),
  setValue: createAction<number>('set value'),
}

const initialState = {pinged: false, value: 0}

const reducer = createReducer(
  on => [
    on(actions.ping, (state, action) => {
      return {...state, pinged: true}
    }),
    on(actions.setValue, (state, action) => {
      return {...state, value: action.payload}
    }),
  ],
  initialState,
)

// you can omit initialState if you want to provide it later
// like using React.useReducer hook: useReducer(reducer, initialState)
// but you might want to provide State as a type parameter to have type checking
type State = {pinged: boolean}
const reducer = createReducer<State>(on => [
  on(actions.ping, (state, action) => ({...state, pinged: true})),
  /* ... */
])

createReducerWithState

The same as createReducer but you can only attach handlers dynamically.

<S>(initialState?: S) => Reducer & {on: Handler<S>}
import {
  createAction,
  createReducerWithState,
} from '@featherweight/actions-reducer'

const initialState = {pinged: false, value: 0}

const reducer = createReducerWithState(initialState)

const ping = createAction('ping')
reducer.on(ping, (state, action) => ({...state, pinged: true}))

const setValue = createAction<number>('set value')
reducer.on(setValue, (state, action) => ({...state, value: action.payload}))

// you can use createReducerWithState without providing initialState
// (the same way as createReducer)
type State = {pinged: boolean}
const reducer = createReducerWithState<State>()

helpful types

This package also includes some types, that might be useful in some situations.

HandlerOf type is useful when you want to define your handler outside of reducer

import {
  createAction,
  createReducer,
  HandlerOf,
} from '@featherweight/actions-reducer'

type State = {count: number}
const add = createAction<number>('add')
const addHandler: HandlerOf<State, typeof add> = (state, action) => {
  return {...state, count: state.count + action.payload}
}

// you can use it later in reducer
const reducer = createReducer(on => [on(add, addHandler)], {count: 0})

ActionOf type is useful when you want to use your action creator return type

import {ActionOf, createAction} from '@featherweight/actions-reducer'
import {takeLatest} from 'redux-saga/effects'

type FetchUserPayload = {id: string; search: Record<string, string>}
const fetchUser = createAction<FetchUserPayload>('fetch user')

function* rootSaga() {
  yield takeLatest(fetchUser, fetchUserSaga)
}
function* fetchUserSaga(action: ActionOf<typeof fetchUser>) {
  action.payload.id // string
  action.paylaod.search // Record<string, string>
  /* ... */
}

Package Sidebar

Install

npm i @featherweight/actions-reducer

Weekly Downloads

0

Version

0.4.1

License

MIT

Unpacked Size

28.9 kB

Total Files

14

Last publish

Collaborators

  • dmytro.ulianov
  • drukas