DEV Community

Matteo Antony Mistretta
Matteo Antony Mistretta

Posted on

Inglorious Store: a Redux-compatible state manager inspired by ECS

I love Redux. It taught me how cool functional programming can be. For a long time I even wondered if using functional programming for games could be a good fit. Some examples proved that you can indeed program games in Haskell, Lisp, or F#. But, is it worth it?

A few years ago I had an idea: create my own version of Redux, but tailored for videogames. Thus the Inglorious Engine was born. The proof-of-concept was solid, but then other things got in the way and I abandoned it for quite some time.

Last summer I was asked to speak at a conference, so I brought the engine back. While re-reading its code I said to myself: "You know what? This isn't half bad!" So I went on evolving it. I even created a new superset of JavaScript: IngloriousScript adds vector operations natively to the language, making game logic even simpler.

Then I went back to the state management component, and I noticed something: this can be used for apps too! It could have been a life-saver that time I had to build a React framework for interactive dashboards.

Long story short, this is how the Inglorious Store was born. To me, it's like Redux should be rewritten now that it has been tested in the field for so many years. RTK cuts a lot of the original verbosity, but it also adds some more.

Redux is awesome, because the Three Principles (Single Source of Truth, Immutable State, and Pure Functions) allow us to build predictable, testable, and debuggable logic. But defining the state through reducers is not really intuitive; actions are best defined through action types and action creators; any impure logic has to be moved to thunks or other similar middlewares.

Redux Toolkit (RTK) solved a few of these problems: slices are more intuitive, reducers can be written with mutable code, and actions are easier to define. But then we need to tell apart reducers and extraReducers, use the builder callback notation, and creating async thunks is not immediate. It's an amazing piece of technology, it powers RTK Query, and I use it everyday at work, but it's not really the simplest.

That's probably why so many other state management libraries started to emerge: Zustand, Jotai, Recoil, MobX, ... each one tries to simplify state management. And they do! But they had to sacrifice a few things: limited testability and predictability, not 100% compatible with time-travel debugging, ... Most of these libraries are perfect for small states, like in a Next.js server-side rendered page.

What if we could have a new library that is 100% compatible with Redux, maintains the Three Principles (mostly) unvaried, but allows to do more with less code?

Enter the Inglorious Store. It's basically a drop-in replacement for Redux, so it's 100% compatible with react-redux and the Redux DevTools. The difference is mainly on how you write your code with it.

  1. You can easily manage multiple instances. Say you have a dashboard with some widgets: it's very intuitive to define a state that describes those widgets, and you can even easily add and remove them at runtime.
  2. It lessens the constraint of pure functions, allowing a reducer to dispatch an event. Your code will still be predictable and testable though, because...
  3. Your events are sent to an event queue, like a publisher/subscriber bus. If you use batched mode, you can even process multiple events before updating your state (thus minimizing re-renders).
  4. The store uses an immutable library like Immer (it's actually Mutative, who claims to be 10x faster) under the hood, so you can write your immutable code as if it was mutable.
  5. You don't need thunks or anything. Your event handlers can be asynchronous functions. Just don't try to mutate the state right after a promise because it's not going to work. Just dispatch another event, like you're used to with redux-thunk or redux-saga.
  6. You don't need to define action types and action creators. Just notify events in a concise way.
  7. Are you scared that you will mistype an action name without action creators and action types? Just use TypeScript: the Inglorious Store is completely typesafe, and it's really trivial to add types to your logic (no const increment: CaseReducer<State, PayloadAction<number>> = (state, action) => state + action.payload, just const increment = (entity: CounterEntity, amount: number) => entity.value + amount)
  8. It features built-in events for entity creation and destruction, and even for hot-replacing reducers.
  9. It features lifecycle events for when an entity has been created or destroyed.

The README is pretty exhaustive, so I'm not going to repeat myself here. Just know that it's open source, that I put my heart in it, and that I would really appreciate it if you could try it out and see if it suits your needs.

You can find code examples here:

  • TodoMVC: An (ugly) clone of Kent Dodds' TodoMVC experiments, showing the full compatibility with react-redux and The Redux DevTools.
  • TodoMVC-CS - A client-server version of the TodoMVC, which showcases the use of notify as a cleaner alternative to dispatch and async event handlers.
  • TodoMVC-RT - A "multiplayer" version, in which multiple clients are able to synchronize through a real-time server.
  • TodoMVC-TS - A typesafe version of the base TodoMVC.

Happy coding!

Top comments (0)