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.
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.
I set a few goals for state manager I wanted:
- Must have actions (in a sense that it would be the only way to modify state);
- 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;
- Must be typed via TypeScript, I mean when working with data, there must be some guards and of course that sweet autocomplete;
- 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);
- Must be dead simple so any junior developer can get productive right away;
- 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.
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.
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).
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).
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:
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.
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.
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.
I'm really excited for what future brings. For now I'm gonna go dogfeed and battle test this library. See ya later.