52 comments

[ 2.9 ms ] story [ 122 ms ] thread
I'm one that loves simplicity in things. So despite the plethora of state management libraries for React, I have always wondered what the absolute "simplest" approach would be.

I wrote this library with the hope that this might be it. Any ideas/suggestions how to further simplify it would be super!

I really like what you have done here. I have two questions:

1. Do you mind sharing examples with async calls? This is very common and I really look for this example as quick as possible

2. How you do connect two stores? Say one triggers and update in the other...

Thanks a lot :)

Looks kind of similar to something Mithril does (or used to do). Ergonomically, I prefer `counter(0)` to `counter.set(0)`.
(comment deleted)
It’s not clear how to define a new entity out of another one using a selector. So perhaps instead of/in addition to `x.use(foo)` you might want `x.select(foo).use()`.

Not sure if simpler but something I’d be missing/would feel inconsistent.

Hi. SimpleR State makes use of selectors without creating a new entity (unlike atoms in Recoil, for example). It uses the more classic paradigm used by popular libs like React Redux where you just use selectors to filter/transform the "received" state to the format required by the component, and not to "create" a derived state per se. I hope it makes sense.
Congrats on launching :) How does it compare to React Context that is built-in?
With this one, you can write granular shared states (multiple "entities") without creating layers and layers of Context Providers in your component tree.

Since data is not encapsulated inside React component tree, I found it to be multiple times faster than when using Context.

And one thing that's perhaps the most obvious... it's simpler because you don't have to use Providers at all.

> Since data is not encapsulated inside React component tree, I found it to be multiple times faster than when using Context.

What do you mean by data being encapsulated in the tree? How does that make it slower?

Maybe "encapsulated" is not the right term when I simply meant "scoped".

I tried scoping the entities using Context in some prior art which we use in production. Some colleagues kindly gave me some benchmark results and the version without Context (external/module-scope) was faster.

I'm not trying to debate against Context here. In fact, there's nothing that would stop anyone from using SimpleR State's entities with Context API.

Not that I'm a fan of Context (or even React); but without publishing the benchmark code or specific technical arguments, it's hard to support a claim about slowness or of speed.
Yup agreed. That's why I stated it as "I found it to be...", coz I'm not trying to convince everyone else LOL.

It's never my intention for this library to try and be the "best" out there. I simply want to create what I sought for, which is the closest to the simplest approach I can think of. And to share it to the public, in case someone out there is looking for the same thing I am, and maybe it could help that someone. That's what open source is for, after all.

Thanks for the answer.

> multiple times faster than when using Context

Oh cool! Do you have some benchmark results for that? Would be awesome to post them in the README if you can.

> simpler because you don't have to use Providers at all

I think the Provider pattern is part of the functional programming pattern with React, so while I do agree it might be harder to get used to but it underlines the no side-effects affecting the state of a given React components and its children.

At scale proper action, reducer pattern is helpful to manage a complicated state tree to still make it functional, easily testable and giving the same end result as a function of state.

So this makes things very similar to how Vue does things. Vue 3 with its encapsulated reactive library also works this way. But immediately I wonder how it works with React innards, do I have to optimize rendering? Does it do wasteful re-renders? How does it affect diffing?
I used to see a lot of online sentiment that Vue would "take over" where React is used now. Is this still the case?
While I finally bit the bullet and learned redux a couple years ago, I tried for a long time to avoid it. It’s overly complex for the tasks it is often trying to solve.

I like this solution because it’s simple, testable, and global.

> It’s overly complex for the tasks it is often trying to solve.

That's a bit like saying a excavator is a bit too complex to hammer nails. Yes, that's true, but that was not why we created the excavator in the first place.

Same with Redux. Initially created to get rid of big, hairy and complex balls of state. If you use it in your basic CRUD, you're probably using the wrong tool rather than the tool is wrong itself.

Each tool has it's place in our toolboxes. It's our job to decide when to use what tool.

How do you define "simple"? Simple is a very complex topic :)

For example, in React the common way of using hooks is `useMyhook()` where "Myhook" is whatever you name it, like "useState", "useEffect", etc.

So I'd argue that an API that follows the conventions in a platform is simpler than one that rewrites them. In this specific situation in the first demo, I would expect to have a hook called `useCounter()` instead of `counter.use()`.

Furthermore, since we are manipulating state again the convention in React for state manipulation is to return the value and then the setter, like:

    const [counter, setCounter] = useCounter();
Now I understand since it's a global state we cannot give it a local initial value to avoid race conditions, so that's IMHO about as simple as you can get.

I didn't do that, but I believe I got pretty close with my own React state management library https://statux.dev/:

  const [counter, setCounter] = useStore("counter");
Following React conventions is certainly an option and is supported by SimpleR State's unopinionated and flexible syntax.

1. It was inadvertently removed from the documentation, but these are equivalent:

   const count = counter.use()

   const count = useEntity(counter)
but the `counter.use()` was chosen in the documentation due to more feedback that requested this "simpler" format (i.e. no need to import `useEntity()`. But again, to your point I will restore the mention of `useEntity` in the documentation.

2. As for the [state, setState] convention, think of useEntity more like useContext than useState. Then it gets simpler.

3. I would also argue that since this is "shared" state, the developer might (again, the library is not opinionated on this) choose to separate the logic of updating shared state. For this reason, I didn't see any reason to provide a setter through the hook, when you can access the setter from anywhere via something like counter.set(). Conforming to useState convention was not justified in this case, especially since useContext convention equally makes sense for shared state.

4. To suit whatever convention, you can always do something like this alongside the definition of the entity:

   export const useCounter = counter.use

I want to point out that SimpleR State provides the flexible constructs to allow the developer to tailor it to their preferred convention.

Thanks for your feedback.

Hey, I like your version too. I’m digging these simple state libraries. Really proves just how absurd Redux was when you finally see the simple solution.
I believe Redux is good when you have 3+ people working on the same project front-end, and absolutely necessary when you have 10+. This makes it work great with the Facebook, Airbnb, etc. of the world.

Which is totally fine, but not your everyday problem for the rest of us! I'm very happy to see all of these versions pop up as well.

What makes that objectively true? I’m looking at these APIs, and at least two of these solutions are plain better design.

What about this would get worse with more engineers or higher caliber engineers that Redux clearly avoids?

It formalizes how data is created, accessed and modified. Instead of multiple places trying to modify some specific data ad-hoc and with possible collisions/changes, now you have a mores strict way of dealing with data.

Basically the Flux architecture. See from 10:20:

https://www.youtube.com/watch?v=nYkdrAPrdcw

I see what you're saying here, and actually agree at some level.

But there is always an alternative, IMHO. If the project has a good code structure and follow equally good coding standards and review process, using simple un-opinionated/flexible libraries would not be a liability with "scale" (in terms of team/project size).

And there's one advantage of simpler approaches for growing teams: faster/easier onboarding. Let's face it, even good React/Redux developers can understand simpler code faster.

Just an alternative thought. But I completely get your point.

Ok, so what are the actions/reducers nonsense about?

I’m just not seeing the argument here, and I’ve been on multiple redux applications on teams of various sizes. We always seem to create layers of indirection to do the simplest possible state update.

We adopted Dan Abramov’s mental model, and my god, it was a labyrinth.

You might want to read my "Tao of Redux" posts and Dan's "You Might Not Need Redux" post, which explain why Redux was designed the way it is:

- https://blog.isquaredsoftware.com/2017/05/idiomatic-redux-ta...

- https://blog.isquaredsoftware.com/2017/05/idiomatic-redux-ta...

- https://medium.com/@dan_abramov/you-might-not-need-redux-be4...

Ultimately, it's about putting a deliberate separation between "what happened" in the UI and "how the state is updated", writing as much of your state update logic as pure functions, and being able to use that indirection to enable tracing state updates over time via the DevTools.

Also note that our official Redux Toolkit package is now the standard recommended way to write Redux logic, and it simplifies most of the Redux code you'll write:

https://redux.js.org/tutorials/fundamentals/part-8-modern-re...

I think you guys should take a more honest retrospective on an age old topic of ‘Intent versus Impact”.

The truth of the story was that community was desperately looking for a solution to global state to avoid prop drilling. No amount of technical virtuosity, or cop outs to a half assed flux architecture that fundamentally mimics model-view-controller or a pub/sub pattern will be satisfiable to anyone with critical thinking ability.

The kids had one job, get some global state available to the app, and they managed in their bravado to pack in ‘addons’, or in sales terms, they ‘up-sold’, that $40 hdmi cable along with your new $1600 television.

Dan packaged his extra bullshit (the reducer pattern) over to a desperate group that had nowhere else to turn for a global state solution. Celebrity and hero worship was one of the premier sins in all of this. The guy didn’t know what the fuck he was doing and no one called him out on it.

Certainly we are not going to marvel at the fact that he is a React core developer. Bad ideas are bad ideas, regardless of the current class that ordains it.

For this, I am depressed, and will continuously antagonize the Redux team for the damage they caused the entire community.

The Redux team owes a debt. Rewrite your library so that the api is 5 lines at max. Replace your conceptions, seek forgiveness for the sin, and let it rest. Until then, don’t peddle your Frankestein ever again anywhere, you all had plenty of time in the limelight and quite frankly - you weren’t good enough.

Like what is the redux-toolkit? Do you guys lack the conviction to decide what the api is or isn’t?

With all that said, if you don’t understand what I’m saying, then mapDispatchToProps or mapsTateToFuckYou, connect everything to nonsense, and I suppose in your new bullshit, createSlice to shut up already. You guys can’t write an api if your life depends on it. And I swear to god, if I hear about the stupid Ducks pattern ... I just can’t anymore.

Anyways, I’d prefer never to punch down if I can avoid it. The redux/abramov/constituents of the react community became the Zeitgeist of the Javacript community. Paragons. And no one stood up to them and behind the bullshit is a clusterfuck of shitty apps. You guys ain’t a small agenda, and I expect the fight to continue.

I'm genuinely sorry that you're angry enough to rant like this. You might want to look into some therapy or something - that kind of anger can't be healthy.

In the meantime, we're going to keep working to support our users. Who, based on the vast amount of feedback we've gotten over the last couple years, _are_ happy with the directions we're going.

(comment deleted)
If that’s the closest thing I’ll get to a sorry for Redux existing, I’ll take it.
Simple state libraries are great for simple web apps, not for massive, enterprise-level applications. As someone who regularly investigates alternatives to redux, it still sits at the top of state management.

These intro how-to comparisons don’t do redux justice because being simple to use is not the stated goal of redux. Large scale, maintainable, and traceable code is the primary goal of redux. When you allow anything to update your state with zero form of traceability, you’ll start to see the cracks in these simple designs.

Are these new state management libraries easy to use? Sure, but what happens when you have tens of thousands (even hundreds of thousands) of lines of code to deal with and then add major refactoring objectives in the pipeline?

Like others have mentioned, if you are using redux for a simple to-do app, then its design principles are probably not as valuable.

It’s like thinking about time complexity for algorithms, at a small enough N, even slow algorithms can compete, but as you increase N, some algorithms become untenable.

You are not the only one debugging large apps. How does the wider world of programming debug stuff without actions and reducers?

It’s crazy how the Redux people either always appeal to authority (‘my app is bigger than yours’) or can’t say anything more than ‘I just like it’.

And to play the rhetorical game, what happens when you have hundreds of thousands of lines of insane Redux code where you have lost all sense of the state tree and need to mentally cordon off one single piece of it to not go insane? Kind of like how libraries like this are already doing it.

So here is my challenge to you. Spec out an app that you believe can only be solved with Redux at scale (or rather, highlight the areas of the app that absolutely benefit from Redux, but please provide as detailed a spec as you can if you have the time, or care for that matter). I’d love to hear about this incredible web app.

The reason is because the problems that surface at scale are difficult to illustrate in a comparison blog, which is the foundation for all the rants against redux.

Redux is not complex, contrary to popular belief. The core code can be written in 100 LoC.

The real downside to redux is not the boilerplate that is required to set it up or the number of concepts to learn, instead it’s how easy it is to do it incorrectly. The redux maintainers have spent the last few years not making changes to the core library, but figuring out conventions and helper libraries to make it more clear what you should (eg normalize state) and should not (put every piece of state) in your redux store.

No offense but the spec exercise seems like a waste of both of our time. No amount of talking about an app can illustrate the code organization at scale.

If you missed it rematch is very promising: It has the feature completeness and interop of redux while being much simpler https://github.com/rematch/rematch (though I find Mobx to be perfect)
For folks who use Redux, the React Redux library has gotten much much more developer-friendly than it originally was. The other libraries like Easy Peasy and Rematch serve a nice syntactic sugar, though.
Out of curiosity, any specific features of Rematch and Easy-Peasy that you specifically like that aren't covered in Redux Toolkit? I've actually got an RTK issue open asking for ideas and concepts that we can learn from them:

https://github.com/reduxjs/redux-toolkit/issues/527

Actually I was favoring the official React Redux stuff in my comment. Just trying to balance it out by mentioning that Rematch, Easy-Peasy and other libraries in the ecosystem all have their own place in the open source world, even if they can be considered different flavor icings on top of the same cake.
What's wrong with @redux/toolkit ?

Just incrementally add reducer and you're good to go. With redux-thunk, devtools built-in, i don't need much more.

Better state management to replace redux to me requires another approach (maybe atom like ?)

I use Redux not (just) because its ecosystem and tooling. It's simply just because i found its API simple to use and scale.

(comment deleted)
This is a general question, but how do you structure state with hooks if your React app is mostly composed of hierarchical, dynamic components?

For example, let's say the initial UI lets you create one or more of Component A (only one type of component); Component A can create one or more Component B (which can be many types of components); and Component B can create one or more of Component C (which can be many types of components).

The app is to model some configuration for some physical devices that are interconnected. I have something working today using the "useImmerReducer" package and updating parent components whenever children components change, but was curious if there is a more established pattern?

Depending on which principles/patterns you are applying, you can go as elaborate as using something like Recoil for that (I think this might be a great application of Recoil as it deals with atomic data), or you can choose to go simple with a tree-type data structure that parallels your component tree.

But first thing you should identify, IMHO, is which of those states are _really_ "shared" states. Coz some of those state values are probably better kept local.

I was looking at Recoil this weekend and it does seem like a good use case, I just wasn't sure how it would simplify what I was doing.

I think I may have applied a bad design as I converted all of my React Components to React functions. Is it possible to instantiate Recoil atoms inside of for loops? Basically, I would like to be able to scope state locally to React functions, but hooks don't let you do that, they have to be completely abstracted.

It would be counter-intuitive if I endorse a Recoil-based solution on this thread that talks about a different library, LOL.

But since I want to help as much as I can, my advice is, regardless of which library you choose, or which approach (atomic vs. monolithic/structured)... start with analyzing which parts of the state in your app really are supposed to be "shared" state. I have a feeling you may be trying to use "shared" state libraries to manage state that only the individual components would actually need. Do those dynamic components actually need to share data? The answer to that is the first important consideration, IMHO, before you can go forward.

Good luck.

hi thanks for building this - i'm struggling with finding a good state management react workflow too. could you speak a bit about the other alternatives that are around and when Simpler is better, and when it isn't? Would help me have more confidence to pick this up.. thanks!
Hi. There are LOTS of much more popular libs you can choose from. My library does not intend to compete as the "best" library, but it has a very specific set of goals, the topmost of which is simplicity through a minimalist API.

Don't get me wrong. SimpleR State is a "complete" library despite the simple API. It supports things like selectors, async actions, and plug-ins. And soon, it will come with built-in plug-ins like persistence, Dev Tools, validation, etc.

Everyone has different priorities when choosing a library, so I suggest going through the design goals that I highlighted in the README file, or complete documentation here:

https://simpler-state.js.org

and see if it fits what you're specifically looking for in a library.

At the end of the day, all these libraries are just React code. In that sense they differ in terms of patterns/principles behind their implementation, but maybe more importantly, in the API/syntax (which is what immediately matters to the developer using it). This is what I can confidently say that SimpleR State delivers... one of the simplest APIs you can find.