Redux reduce boilerplate library
Powerful and simple
redux middleware to handle async actions.
peer dependencies: redux: ^4\
optional dependencies: rxjs: ^6
store/index.ts
// Define your inner stores
interface RootStore {
inner: InnerStore
}
// Combine it
const reducer = combineHandlers<RootStore>({
inner: innerHandler
})
const store = createStore(reducer.buildReducer(), applyMiddleware(handlerMiddleware()))
store/handler.ts
export const { handler } = create<RootStore>()
Define store & handler:
interface Store {
counter: number
}
const myHandler = handler<Store>({ counter: 0 })
// Create action
const myAction = myHandler
.action() // For arguments use `.action<TArgs>`
.pipe(
// Operators
)
Each pipe must contain single main operator
Handles standard action handler.
Compatible operators: Common
sync((state, { args, type }) => typeof state)
Handles rxjs observable.
Compatible operators: Common, Async
rx((args, { action$, getState, type }) => Observable)
Handles promise.
Compatible operators: Common, Async
promise((args, { getState, type }) => Promise)
Handles async dispatch.
Compatible operators: Common
thunk({ dispatch, getState, args } => void)
Occurs before async method is called.
pending((state, { args, type }) => void)
Occurs on async method succeeds.
fulfilled((state, { payload, args, type }) => typeof state)
Occurs on async method failed.
rejected((state, { error, args, type }) => typeof state)
Occurs after async method is completed.
completed((state, { args, type }) => typeof state)
Sets the property = true
on pending.
Sets the property = false
on completed.
loading(prop: keyof state)
Prevents calling actions based on state.
Can be used only before main operator.
available((getState, { args, type }) => boolean)
myHandler.handle(
myAction,
// ...operators[]
)
import { actionSanitizer } from 'redux-handler/utils'
const composeEnhancers: typeof compose = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ actionSanitizer })
: compose
export const store = createStore(reducer, composeEnhancers(...composes))
Simple users fetch:
export interface UsersStore {
data?: any[]
loading?: boolean
}
export const usersHandler = handler<UsersStore>({})
export const fetchUsers = usersHandler
.action<{ limit: number }>()
.pipe(
available((getState, { args }) => getState().users.data),
rx(({ limit }) => ajax({ url: `/users?limit=${limit}` })),
fulfilled((s, { payload }) => ({ ...s, data: payload })),
loading('loading')
)
Dispatch another action inside action:
export const updateUsersBalance = usersHandler
.action()
.pipe(
sync(s => ({ ...s, balance: s.users.reduce((a, b) => a + b.balance, 0) }))
)
export const fetchUsers = usersHandler
.action()
.pipe(
rx(
() => concat(
ajax(),
of(updateUsersBalance())
)
)
)
npm run build
\npm publish dist