redux-instant

Create type-safe Redux action creators and reducers with minimal boilerplate.

Usage no npm install needed!

<script type="module">
  import reduxInstant from 'https://cdn.skypack.dev/redux-instant';
</script>

README

redux-instant

Minimize boilerplate in your Redux app by inferring action creators and action types from your reducer with precise type-checking when you use Typescript.

Actions created by this library are serializable, and are compatible with time-travel debugging.

For a working example see examples/todo-app. Run the app with yarn start.

Reducer and Action Creators in a single step

In a typical Redux app you define a reducer function that uses a switch statement to handle specific action types. With redux instant you provide an object with a separate handler function for each action type. Each handler takes app state and an action payload as arguments, and returns an updated state.

const handlers = {
  create(state: State, todo: { complete: boolean, title: string }) {
    return {
      ...state,
      todos: state.todos.concat({ ...todo, id: getId() })
    }
  },
  remove(state: State, id: number) {
    return {
      ...state,
      todos: state.todos.filter(todo => todo.id !== id)
    }
  },
  toggle(state: State, id: number) {
    return {
      ...state,
      todos: state.todos.map(
        todo =>
          todo.id === id ? { ...todo, complete: !todo.complete } : todo
      )
    }
  }
}

The object key for each handler is used to generate the type property for the corresponding action. The keys here (create, remove, toggle) are short for easy reference. To avoid name collisions with Redux actions defined in other modules the action names are combined with a unique prefix in the next step.

Handlers are combined to generate a single reducer function, and a set of action creators in one step:

import { combineHandlers } from "redux-instant"

const { actions, actionTypes, reducer } = combineHandlers(
  "TodoApp", // unique prefix to distinguish actions defined in this module
  (): State => ({ todos: [] }), // function to get initial state - make sure to include the type annotation!
  handlers
)

reducer is a normal reducer function; actions is a map of action creators.

Using Action Creators

You can dispatch actions like this:

function ShowTodo(props: { dispatch: Redux.Dispatch, todo: Todo }) {
  const { dispatch, todo } = props
  return (
    <label>
      <input
        type="checkbox"
        checked={todo.complete}
        onChange={e => dispatch(actions.toggle(todo.id))}
      />
      {todo.title}
    </label>
  )
}

Everything is type-checked: if you tried to call actions.toggle() with an argument that is not a number then you will get a type error.

Referencing Action Types

combineHandlers generates a unique string for each action type using the prefix argument, and the keys in the handler map. If you want to refer to an action type use the actionTypes map returned by combineHandlers. Using the example above this is what you will get:

actionTypes.create === "TodoApp/create"
actionTypes.remove === "TodoApp/remove"
actionTypes.toggle === "TodoApp/toggle"