retux

Core library of Retux, a minimalist declarative type-safe(strongly-typed) scalable Redux architecture.

Retux

npm-version npm bundle size

Commitizen friendly Conventional Commits JavaScript Style Guide code style: prettier

Core library of Retux, a minimalist declarative type-safe(strongly-typed) scalable Redux architecture.

Features

  • Minimalist. Retux reduces huge volume of boilerplate code while still gaining better type-infer and auto-completion.
  • Declarative. Action-First desgin instead of Action-Creator-Fisrt makes it clean, less-hacking and easy to read for new contributors(including future-self!).
  • Type-safe(strongly-typed). Retux enforces strict typings. With utilities of Retux you will never lose the strictness of typings while enjoying great flexibility.
  • Scalable. A Retux module can be easily split into isomorphic sub-modules. Retux can also optionly leverage the power of meta-programming on modern engine for further performance boost.

Installation

  • yarn: yarn add retux
  • npm: npm add retux

Usage

See official guide.

API

See docs.

At First Glance

This is the basic structure of Retux architecture(Others see examples).

import { createStore } from 'redux'
import { CreateActionCatalog, ActionHandlers, createReducer } from 'retux'
 
// All actions of a module are defined here.
// This is the core of Retux design.
// Other infomation will be extracted from Action Catalog.
export type ActionCatalog = CreateActionCatalog<{
  INCREMENT: {
    payload: {
      // Comments will also be displayed on the intellisense panel
      /** default 1 */
      step?: number
    }
  }
  DECREMENT: {
    payload: {
      /** default 1 */
      step?: number
    }
  }
}>
 
export const initState = {
  count: 0
}
 
export type State = typeof initState
 
// Action Handlers can be easily splited.
export const actionHandlers: ActionHandlers<State, ActionCatalog> = {
  // Any missing or mistyped action will be caught by ts compiler.
  INCREMENT: (state, { payload: { step = 1 } }) => ({
    count: state.count + step
  }),
  DECREMENT: (state, { payload: { step = 1 } }) => ({
    count: state.count - step
  })
}
 
// Now in the store root:
 
const reducer = createReducer(initState, counterActionHandlers)
 
const store = createStore(reducer)
 
store.subscribe(() => console.log(store.getState()))
 
// Actions are strongly typed. Any mistyped name is caught by ts compiler.
store.dispatch({ type: 'INCREMENT' })
 
// Payload and meta are also strongly typed with the action type.
store.dispatch({ type: 'DECREMENT', payload: { step: 10 } })

Actions

If we need all the actions:

import { Action } from 'retux'
 
export type ActionCatalog = CreateActionCatalog<{
  ACTION1: { payload: boolean }
  ACTION2: { payload: number, meta?: boolean }
  ACTION3: { payload: { val: string } }
}>
 
type ModuleActions = Action<ActionCatalog>
 
// single action
type ActionIncrement = Action<ActionCatalog, 'ACTION1'>

Notice ModuleActions will be

type ModuleActions = 
  | { type: 'ACTION1', payload: boolean }
  | { type: 'ACTION2', payload: number, meta?: boolean }
  | { type: 'ACTION3', payload: { val: string } }

instead of

type ModuleActions = {
  type: 'ACTION1' | 'ACTION2' | 'ACTION3',
  payload: boolean | number | { val: string }
  meta?: boolean | undefined
}

Action Creators

Retux is Action-First. All Action Creators are generated from Actions.

import { createActionCreators } from 'retux'
 
const action = createActionCreators<ActionCatalog>(actionHandlers)
 
dispatch(action.ACTION1(true))

That's it! Later on if you want to replace an action with thunk etc.

const action = createActionCreators<ActionCatalog>(
  actionHandlers,
  {
    ACTION1: (payload: boolean): MyThunkAction<'ACTION1'> =>
      dispatch => dispatch({ type: 'ACTION1', payload })
  }
)

Retux also offers proxyActionCreators for modern engines which does the same thing except action creators are lazy created on first visit.

See the guide for all the features of Retux.