Skip to content

gingerich/redux-remodel

Repository files navigation

redux-remodel

Create reducers from simple declarative data models

The motivation behind redux-toolkit is to enable simple intuitive reducer composition and avoid boilerplate code. A reducer is a simple functional building block which makes it endlessly composable. However, handwriting traditional redux reducers can be verbose and cumbersome. Instead redux-remodel lets you declare a reducer as a model with actions, computed properties, and sub-models (i.e. slices). The resulting reducer function is also decorated with action creators for convenient action dispatching.

Under the hood redux-remodel uses Immer so you can write mutative actions and still preserve reducer purity and state immutability. Using Immer also enables structural sharing which reduces the memory intensity of traditional redux reducers. Action creators are built with createAction() from @reduxjs/toolkit and computed properties rely on the Reselect utility.

NPM Build Status JavaScript Style Guide

Install

npm install --save redux-remodel

Usage

Define a todos model

const todos = {
  actions: {
    addTodo: (todos, { payload }) => {
      todos.push({ id: todos.length + 1, title: payload });
    },
    toggleTodo: (todos, { payload }) => {
      const todo = todos.find((t) => t.id === payload.id);
      if (todo) {
        todo.completed = !todo.completed;
      }
    }
  }
};

Declare a new model by composing todos

import { createModel } from 'redux-remodel';

const initialState = { view: 'all' };

const app = createModel(initialState, {
  slices: {
    todos: createModel([], todos)
  },
  actions: {
    setView: (state, { payload: view }) => {
      state.view = view;
    }
  },
  computed: {
    active: ({ todos }) => todos.filter((t) => !t.completed),
    completed: ({ todos }) => todos.filter((t) => t.completed),
    visibleTodos: (state) => {
      switch (state.view) {
        case 'ACTIVE':
          return state.active;
        case 'COMPLETED':
          return state.completed;
        default:
          return state.todos;
      }
    }
  }
});

Use like a reducer function

let state;
state = app(state, { type: 'addTodo', payload: 'My Todo' });

console.log(state);
/*
{
  view: 'all',
  todos: [{ id: 1, title: 'My Todo' }],
  active: [{ id: 1, title: 'My Todo' }],
  completed: []
}
*/

The returned reducer is decorated with action creators

app(state, app.actions.addTodo('My Todo'))

License

MIT © gingerich