DEV Community

Cover image for Announcing new and simple state manager “Exome”
Marcis Bergmanis
Marcis Bergmanis

Posted on • Updated on • Originally published at marcisbee.Medium

Announcing new and simple state manager “Exome”

TLDR: I created new state library called Exome (works with React and there's a PR for Vue too (excuse me, that sounded like a pokemon)). The end.

Introduction

Let me introduce myself first. I'm working as a front-end dev for as long as I can remember. Since before the days of jQuery dominance even. So I've seen a lot from dead simple JS libraries to utterly complex ones and again back to simple ones and so on. When I started learning Redux, NgRx and Vuex back in a day it was a high hill to climb at first always wishing for something easier. Much easier, like a plain object being state easier.

So I started tinkering with different ideas of how I would like my ideal state manager to look and feel years ago. I remember having random ideas at random times and just dropped them in notes. I never did anything with those because none of them really felt good enough.

Until now when I reached a point when starting new project where working with deeply nested tree structures was a must have. Since that is one of Redux (and I'd say the whole flux architecture for that matter) core problems, I went with valtio, a library by Pmndrs. And it was great at first, but then I started to see a real mess in code with my made up actions and selectors making sense less and less. And line between what gets selected from state and what updated became blurred together. So I knew that I had only one option, to finally figure out my dream state manager.

From idea to library

I set a few goals for state manager I wanted:

  1. Must have actions (in a sense that it would be the only way to modify state);
  2. Must integrate with Redux Devtools and to see deeply nested objects right away, not clicking through tree to find exact one I'm looking for;
  3. Must be typed via TypeScript, I mean when working with data, there must be some guards and of course that sweet autocomplete;
  4. Must be able to save and load as a string (since in my case I needed a way to save state in a file and restore it);
  5. Must be dead simple so any junior developer can get productive right away;
  6. Must not bottleneck the view renderer (must be performant).

So those were my initial goals and in a single evening I came up with Exome and developed a prototype. I was so happy with it that I decided to make it open source. It really hits all of the goals I set and more. Only issue is that it doesn't support IE11, since it uses Proxy. But in my case it didn't matter. Not true anymore! It support IE11 too.

Examples

So to start off here's a primitive counter example (click on number to increment it).

And that's it, no providers, no contexts, no boilerplate. Store is just a class. Properties are state values and methods are actions. Whenever action is called, state is updated and wired view components (via useStore) are rendered. It updates only those components that uses particular Exome instance in useStore hook. It's simple as that.

Ok everyone can do a counter example. What about that deeply nested tree part that was in my goals. Aah great question. So I threw up a simple router example for this.

This is more complex one. I wanted to demonstrate here that we can have Store that can have multiple Tabs and those Tabs can also have multiple Items. So it's a nested tree. Just pass child Exome instance through useStore and that child component will be wired.

Devtools

Working with Redux Devtools to examine Exome state is a breeze too. No need to dig deep into state tree to find Exome you're looking for. But if you want to see whole tree, that's available too (all Exome childs are examinable in full tree view).

Exome state explored in Redux Devtools

Note: Since devtools have weird bugs when using serializedType every Exome will have $$exome_id until those bugs are resolved.

Saving and loading state is described in details in API docs (https://github.com/Marcisbee/exome#savestate).

Performance

It's great! And will be more and more optimised as library matures.

To get the feel of how this library performs, I created a benchmarks (really, really primitive ones). And the results are quite good, in fact Exome seems to be the fastest library out of what I've tested (at least on my machine — Macbook Pro 2020 M1). And here are the results:

Render component benchmarks

It's a simple counter example in React, that gets rendered in first benchmark. And in second one count gets incremented and rendered via view update.

Increment action benchmarks

Please note that these benchmarks are not intended to make any library look bad (in fact they all are really great). Main intention is to see where Exome stands in terms of Performance and where and how to improve it. And I know those are very primitive benchmarks, but I don't really want to spend a lot of my time to create real world apps for each store. Maybe in future.

Conclusion

There are more perks to Exome that we didn't explore, but most of them are explained in readme.

The most exciting one is that micro-frontends can share single state between them. I created a tiny example where I created single Exome store and passed it to React and Vue. Whenever something changes in state, it gets synced across all frameworks that utilise Exome middleware. It just works.

Vue counter and React counter sharing counter state

I'm really excited for what future brings. For now I'm gonna go dogfeed and battle test this library. See ya later.

GitHub logo Marcisbee / exome

🔅 Proxy based state manager for deeply nested states

Oldest comments (16)

Collapse
 
natalia_asteria profile image
Natalia Asteria

This is... unorthodox and wonderful.

Collapse
 
marcisbee profile image
Marcis Bergmanis

Thanks!

Collapse
 
seanolad profile image
Sean

Dude this is really nice, you might have just created the next great state library😄

Collapse
 
marcisbee profile image
Marcis Bergmanis

Thanks! 😄 I hope people find it useful.

Collapse
 
epavanello profile image
Emanuele Pavanello • Edited

it looks amazing 😵🤔
I’ll use it on the next project, let’s see if it’s easy as it sounds 💪🏻

Collapse
 
marcisbee profile image
Marcis Bergmanis

Thanks! Let me know in github discussions what could be improved 👍🏼

Collapse
 
epavanello profile image
Emanuele Pavanello

What about IE11 bug?
Do you any idea to bypass the problem?

Thread Thread
 
marcisbee profile image
Marcis Bergmanis

I'm using Proxy here that is not supported in IE11.

But I am keen on making it work on IE11 since I know in many companies whose clients use IE11 heavily. Will be investigating this in near future.

Thread Thread
 
marcisbee profile image
Marcis Bergmanis

Got curious and checked.
Actually IE11 support will be very easy add since I'm using very small part of Proxy Object and that part is supported via Proxy Polyfill.

Collapse
 
marcisbee profile image
Marcis Bergmanis

I'm happy to announce that Exome now supports IE11 too 🥳
github.com/Marcisbee/exome#ie-support

Collapse
 
aminnairi profile image
Amin

Did you try to share states between React and React Native? Is this something you want to support any time soon?

Collapse
 
marcisbee profile image
Marcis Bergmanis

I haven't tried it with React Native yet. It's in my todo list.

What would be the use case of sharing state between React Native and React?

Collapse
 
aminnairi profile image
Amin

A game between two person but I guess we are stuck with a client server architecture in the end for now. Or maybe have some sort of helper that can be used in the server to not have to deal with all that. That would be awesome.

Thread Thread
 
marcisbee profile image
Marcis Bergmanis

Yes, this probably have to be a server talking to clients. But Exome probably could have some sort of helper you mentioned. Will write this down as a feature to have in future.

Collapse
 
sirseanofloxley profile image
Sean Allin Newell

Can confirm, is very easy and nice to use! I was a little worried about async/promise stuff, but basically just kick off a promise from some event and then call the methods and it all works 👍👍

Collapse
 
marcisbee profile image
Marcis Bergmanis

Thanks for giving it a try!
Just realised I should probably add some examples with promises in readme, totally forgot.