39 comments

[ 3.2 ms ] story [ 72.3 ms ] thread
After lots of tweaks based on the valuable feedback from the community, SimpleR State is finally at its first official stable release v1.0.0.

Big thanks go to the great people that contributed to this effort to bring it to Production grade. I can now confidently call it the "simplest" out there. Mission accomplished.

This looks fantastic! After twisting my brains over redux, this looks like a breeze to work with!
It looks promising. I wonder how it will play with TypeScript. Would need to dig deeper. However the angle to make it simple is great. No boilerplate code, unit testing as it should have always been. Great work!
Happy to say that this also works well with TypeScript with minimal explicit types required. My main goal is to maximize the use of type inference. The documentation provides TypeScript version of each example (outside of the TL;DR cheat sheet), except in cases where TS version is the same as the JS due to type inference. Even where there is TS version, the difference from JS is minimal.
Usage feels very similar to redux when using redux toolkit, and the latter comes with a more mature ecosystem.

There was also nothing about streams and such that you would normally handle with redux-observable or redux-saga.

One of the super powers of redux+saga is being able to listen to every state update (and act according to that), this doesn't seem to be possible (unless you make a super entity I suppose?).

A quick glance at the documentation showed that you can write plug-ins to do such things as listening to all state changes (unless I misunderstood something). See the logging plugin example.

Without a Chrome dev tools extension though I don't think I could leave redux just yet.

Happy to hint that there is an upcoming plug-in to support Redux DevTools too. Soon.
Would it work with server side rendering?

Usually there would be a problem importing/defining things from static files like that as this would be shared within the server between instances of renderings.

SimpleR State acts as a "module-level store", one of the reasons why it's so simple. When you start doing SSR, though, as with anything else in React global state management, you would sacrifice some of that simplicity.

So to answer the question, yes SimpleR State works with SSR, but you need to take an extra step. You would need to somehow "scope" your global state inside the React component tree. The most common solution is using a Context to store your server-side entities, so that they are not "shared" among SSR requests.

SimpleR State proclaims itself as "unopinionated", so the preferred SSR solution is a decision that the developer has to make. It would be worth adding some options in the documentation, though. So thanks for bringing this up. I also received a few requests on such documentation in the previous Request For Comments during the beta run. A doc page on SSR is underway.

Seems like the simplest state management API to wrap one's head around that I've seen so far.
Looks great. The API reminds me of the new Vue 3 Composition API.
Very nice!

I've been prototyping something server-side in the style of the SQL Kappa architecture: facts on immutable logs, possibly keyed (and compactable by key), and SQL materialized views streaming the updates with Flink. And I'm in search of a React state manager in that philosophy.

Rows are sent to each client requesting a feed of them:

    INSERT INTO user_orders_total_feed(user_id, data)
    SELECT 
      uo.user_id,
      SUM(o.price) total
    FROM user_order uo
    JOIN order o ON uo.user_id = o.user_id
    JOIN user_live_subscription uls ON uo.user_id = uls.user_id
    GROUP BY uo.user_id
    WHERE uls.subscription_topic = 'orders_total'
But: maybe you want all the orders in the UI and do the aggregation client-side, to have all orders alongside the total. You could move the aggregation to the client side. And it would be great to have the same data language than on the server (so that business rule expression is the same).

In that case, a React Hook library like SimpleR State could aggregate in SQL the sum and update the view reactively. It would look like:

    const TotalView = () => {
      const view = state.view('SELECT SUM(price) total FROM order_feed');
    
      return (
        <>
          <div>{view.total}</div>
        </>
      )
    }
I don't think that would be easy to do; but I think there is a market for it: The business folks doing no-code+SQL client-side, and they would sometimes ask through proper channels for a query to be computed globally server-side or be brought client-side.
Although this is not provided by SimpleR State out of the box, the library makes it easy to implement a "selector" function that accepts an SQL query, and pass this selector to the entity hook. This SQL query syntax in React can well be a library on its own, I suppose.
Would that be the transform function from here [1]?

I do agree too that this definitely should be a library on its own.

Anyway thanks for your work; I'll definitely check SimpleR in my project.

[1] https://github.com/arnelenero/simpler-state/blob/f6336817c9e...

Yes, I called it "transform function" in the documentation to reflect the more general functionality that also includes computed values, apart from selection ("picking" properties).

Thanks for your encouraging feedback.

I think SQL on the frontend is a big opportunity. It’s a powerful declarative language that matches nicely to React.

Every component’s data requirements are essentially an SQL query, and a lot of client-state could probably be represented in further SQL queries (filtering, aggregation).

Doing joins makes things much more consistent too when the same entities are referenced in multiple places in the UI.

Just like React works declaratively by re-rendering the virtual dom, wouldn’t it be great if we just re-ran a declarative query for the data too?

Kind of like Redux (global state) I guess but a relational model version.

IMHO, since react hooks hit the market, state management libraries became kind of obsolete.
We tried this approach and have attempted to bend React Context to our need, but it feels like its gotten pretty hacky. Are you only using component state and Context or what is your approach?
Not who you asked, but: We use useContextSelector to ease context rendering redundantly and to make it clearer what a component uses within a context.

It isn’t native to react, but it appears it will be in a similar form in the future.

It has been a joy to use so far. Having server side state working so seamlessly with the client is a great feature for us.

I haven't tried this one, but for what it's worth, if you're looking for a simple state manager, I LOOOOOOOOOVE Pullstate.js. I've been using it for a reasonably complex frontend and it's just excellent, an absolute breeze to work with. For reference, I'm the type of person who appreciates the power of redux but views it as conceptually overcomplicated to the detriment of the developer experience-- I felt it was overhyped and added limited value relative to the cost of its complexity. (Not that it's that complex, but more than a state manager needs to be.)

Pullstate is extremely simple but just works perfectly.

That looks really good. I like how the hook is built into the store API.

My problem with a lot of these simple state managers is they often just stop short of handling asynchronous state. I think it should pretty much be mandatory to handle pending and error states on promises for state manager nowadays. Pullstate's Async Actions look pretty good for that.

I've been using Effector a lot recently because it has just a bit more power in managing the interactions between stores and events but if I needed something more straightforward Pullstate would be good. Thanks for the tip!

If you like this API but are already using Redux (or still want to use Redux), I've found Rematch[1] to be a very productive wrapper around redux that has a DX almost identical to this library.

[1] https://rematchjs.org/

reminds me of mobx (which I find very simple). an entity is just like an observable, no?
I'm an avid user of mobx and yes, this is similar, but no where near as powerful, and much less magical. For example, in no particular order:

* mobx is smart enough to know when only one property of a complex object has changed and that that component depends only on that property. So you can have complex state objects, and if your component only uses property X of said object, it only re-renders if X changes but not Y.

* mobx allows you to compose observables - any observable can reference the value of any other observable and if that second observable changes, the listeners for the first observable will re-render, etc. This means state can depend on other state, and everything updates correctly. For example, if you have a store that fetches profile data for the auth user, and the auth user id changes due to login or logout, the profile data store can trigger a refetch, all this happening outside the react component tree, then finally the correct components re-render.

* mobx works on it own and is agnostic to framework (react/vue/etc) and is integrated via binding libraries for each framework.

* mobx provides quite a few different kinds of observables with different behaviors (shallow maps, lists, etc)

* mobx allows you to declare arbitrary reactions that run automatically when state changes, whereas this library here really just re-renders components when state changes.

* With mobx you don't even have to say which things in your component use observables - you just wrap your component in observer(), and any time it references an observable, mobx tracks it and will re-render the component if the observable changes. Whereas here you need explicitly hooks for each state atom.

There are many more differences, in every case mobx does more. So mobx is a far more complex and powerful system, whereas this is a simpler system that extends useState() hooks with objects that live outside the component. Which is not a value judgement, use the simplest tool that meets your needs.

Have been using mobx recently and wrapping your component in observer() and seeing it update when the observable changes, feels truly amazing.
There are tons of small simple state management solutions for react coming from all the people who hate redux's complexity and lack of async actions

Well done for getting some community traction!

I found effector[1] after I started looking around for a state manager which:

1. Either supported multiple stores or provides a way to subscribe to individual branches (as opposed to the entire store).

2. Had an action/event abstraction similar to redux where multiple branches (or multiple stores) could subscribe to the same action and update independently (and without knowledge of invoker).

I have been using it for some time and have had a good experience.

The core is framework agnostic (with additional integrations for react & vue).

I started used it with Svelte and its API matches the expectations of Svelte stores so you can use it without needing any additional integration, which was cool. Later I used it with lit-element through a 5 line custom integration and that worked pretty well too.

[1] https://github.com/effector/effector

Curious if React developers feel like this sort of fragmentation of what tools to use around React is fine, or if it's a pain point for you.
Fragmentation comes with open source. State management is fragmented on the backend as well: PostgreSQL, MongoDB, Kafka, Redis, Dynamo, Influx, etc...it's just that state management fundamentally has different trade-offs based on how you structure it (and may involve 1 or several of these).

If your app is simple, you don't really need a separate state-management solution (just use the built-in React useState and useReducer hooks). If it's complex, you have a choice to make, but that's a fundamental issue with apps at scale and you have to make those choices top to bottom.

I feel like we are still waiting for someone to create a nice multi-paradigm database blending everything we’ve learned in the past 50 years, that can also run in the browser.

I see all state as a stream of events building a graph, then we run queries on that graph. Then it’s all about optimizing the queries which we do.

We’ve kind of shoehorned our use cases into whatever dbs we had available and have probably been overwhelmed by the speed we needed to scale our cloud storage resulting in sub-optimal solutions. Or maybe it’s just wishful thinking that there will exist a db to do it all.

As an outsider to React, it seems significantly less batteries-included and more fragmented than Angular.
The choice of `set` taking a function is a questionable one. I know this is fairly common for JS libraries but IMHO it should only be used when you know that the value can't naturally be a function. Otherwise it is a footgun.

  let someval = () => 1234;
  state.set(someval) // Oops.
You need to remember to write the following if the value might be a function.

  state.set(() => someval);
I would have just made this a separate function and call it `.update()`.