Skip to content

Get rid of your reducer boilerplate! Zero hassle state management that's typed, flexible and scalable.

Notifications You must be signed in to change notification settings

richardcrng/riduce

Repository files navigation

Riduce 👻

Get rid of your reducer boilerplate!

Zero hassle state management that's typed, flexible and scalable.

npm install riduce

Travis (.org) bundle size npm version

Edit Riduce example - MadLibs for Developers

Whether you're using useReducer or redux, reducer boilerplate is tedious to learn, setup and maintain.

What if type-safe state management was quicker, easier and simpler?

Riduce is a library written to be:

  • Strongly-typed, so your state stays predictable
  • Trivial to scale as your state grows more complex
  • Zero hassle, with just two lines of code...

... and one of the 2 lines to setup is an import.

import riduce from 'riduce'

const [reducer, actions] = riduce(initialState)

That's it! Now you've got a type-safe reducer and arbitrary actions, with zero hassle.

Let's see it in use!

🚧 Full documentation for Riduce is under construction - but the API is essentially the same as Redux-Leaves, except riduce replaces the reduxLeaves default export. Currently documented here are indicative examples on setup, usage and customisation. These give quite a lot of information about how the library is used. For more specifics, please consult the Redux-Leaves documentation to see, e.g., the default action creators which create gives access to.

Introductory Example

For a useReducer example, see this CodeSandbox.

For a redux example, you can run this Repl.it.

For more advanced usage of Riduce, see this example.

Below, we'll walk through the introductory Redux example, showing:

  1. Zero hassle setup with 2 lines of code;
  2. Scalable state management with arbitrary actions; and
  3. Typesafe action creators to mirror your state's shape.

Zero hassle setup

Let's imagine we're controlling the state for a museum.

import { createStore } from 'redux'
import riduce from 'riduce' // 1st line: import

const museumState = {
  isOpen: false,
  visitor: {
    counter: 0,
    guestbook: ['richard woz here']
  }
}

const [reducer, actions] = riduce(museumState) // 2nd line: setup
const { getState, dispatch } = createStore(reducer)

And that's it. Those two lines replace all of our reducer boilerplate.

Scalable state management

Continuing on from above, let's:

  1. Open our museum;
  2. Add to the visitor counter;
  3. Sign the guestbook; and
  4. Amend a guestbook entry.

Previously, you might create 4 x reducer branches, action types and action creators.

Riducer gets rid of all that boilerplate.

Now, it's as simple as describing the changes we want to see!

// at `state.isOpen`, create an action to toggle the boolean
dispatch(actions.isOpen.create.toggle())

// at `state.visitor.counter`, create an action to add 5
dispatch(actions.visitor.counter.create.increment(5))

// at `state.visitor.guestbook`, create an action to push a string
dispatch(actions.visitor.guestbook.create.push('LOL from js fan'))

// at `state.visitor.guestbook[0]`, create an action to concat a string
dispatch(actions.visitor.guestbook[0].create.concat('!!!'))

getState()
/*
{
  isOpen: true,
  visitor: {
    counter: 5,
    guestbook: [
      'richard woz here!!!',
      'LOL from js fan'
    ]
  }
}
*/

All this is possible because Riduce's actions gives you loads of convenient action creators out of the box, which you can use liberally throughout your state tree: update, set, filter, reset, and many more...

It's also possible to add your own in, as documented in advanced Riduce usage.

Typesafe action creators

Now we've seen that Riduce is zero-hassle setup for arbitrary action creators without the reducer boilerplate.

It's written in TypeScript, so it's helpfully typed right out of the box as well!

// can we push to a boolean? no!
// ❌ TypeError: (ts 2339) Property 'push' does not exist on type...
actions.isOpen.create.push()

// can we push to an array without an argument? no!
// ❌ TypeError: (ts 2554) Expected 1-3 arguments, but got 0.
actions.visitor.guestbook.create.push()

// can we push a number to an inferred string[]? no!
// ❌ TypeError: (ts 2345) Argument of type '10' is not assignable to parameter of type 'string'.
actions.visitor.guestbook.create.push(10)

// can we push a string to an inferred string[]? yeah, okay then.
// ✅ compiles!
actions.visitor.guestbook.create.push('10')

Get started

You may wish to check out the following:

Advanced Riduce usage includes:

  1. Bundle multiple actions into a single dispatch;
  2. Execute arbitrary reducer logic for extendability;
  3. Add custom reducers for reusability; and
  4. Control action types for debugging (e.g. Redux DevTools).

Have fun adding it to your project!

npm install riduce

🚧 Full documentation for Riduce is under construction - but the API is essentially the same as Redux-Leaves, except riduce replaces the reduxLeaves default export. Currently documented here are indicative examples on setup, usage and customisation. These give quite a lot of information about how the library is used. For more specifics, please consult the Redux-Leaves documentation to see, e.g., the default action creators which create gives access to.