Show HN: SimpleR State – simple state management for React
github.com
github.com
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");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.
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 about this would get worse with more engineers or higher caliber engineers that Redux clearly avoids?
Basically the Flux architecture. See from 10:20:
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.
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.
- 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...
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.
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.
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.
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.
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.
I wrote this library with the hope that this might be it. Any ideas/suggestions how to further simplify it would be super!
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 :)
Specifically, look at these "Recipes" pages:
Not sure if simpler but something I’d be missing/would feel inconsistent.
I like this solution because it’s simple, testable, and global.
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.
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.
https://redux.js.org/tutorials/fundamentals/part-8-modern-re...
https://simpler-state.js.org/recipe-transforms.html
Thanks for your reply.
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?
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 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.
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.
It rely on https://github.com/facebook/react/tree/master/packages/use-s..., which make it safe to use in concurrent mode and the implementation a lot simpler.
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:
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.
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.
What do you mean by data being encapsulated in the tree? How does that make it slower?
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.
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.
> 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.