[HN Gopher] Show HN: SimpleR State - simple state management for...
___________________________________________________________________
Show HN: SimpleR State - simple state management for React
Author : aenero
Score : 41 points
Date : 2021-03-20 19:34 UTC (2 days ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| zoontek wrote:
| I wrote a lib with a very similar API:
| https://github.com/zoontek/react-atomic-state
|
| 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.
| franciscop wrote:
| 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");
| runawaybottle wrote:
| 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.
| qudat wrote:
| 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.
| runawaybottle wrote:
| 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.
| franciscop wrote:
| 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.
| runawaybottle wrote:
| 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?
| franciscop wrote:
| 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
| aenero wrote:
| 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.
| runawaybottle wrote:
| 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.
| acemarke wrote:
| 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...
| aenero wrote:
| 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.
| brainless wrote:
| If you want a simple and elegant state management module then
| check out Zustand. I have been using this for a year or a bit
| more and am absolutely happy with it.
|
| https://github.com/pmndrs/zustand
| The_rationalist wrote:
| 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)
| aenero wrote:
| 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.
| acemarke wrote:
| 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
| aenero wrote:
| 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.
| rtcoms wrote:
| I'm yet to decide between Rematch and easy-peasy
|
| https://easy-peasy.now.sh/
| acemarke wrote:
| Have you looked at our official Redux Toolkit package?
|
| https://redux.js.org/tutorials/fundamentals/part-8-modern-re...
|
| https://redux-toolkit.js.org
| snissn wrote:
| 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!
| aenero wrote:
| 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.
| zeroonetwothree wrote:
| I have liked Recoil, it works really well for things like async
| loading. For more basic stuff I just use the useState hook
| directly.
|
| https://recoiljs.org/
| dkarras wrote:
| 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?
| aenero wrote:
| The optimizations on preventing unnecessary re-renders are
| well-documented here:
|
| https://simpler-state.js.org/recipe-transforms.html
|
| Thanks for your reply.
| benjaminclauss wrote:
| I used to see a lot of online sentiment that Vue would "take
| over" where React is used now. Is this still the case?
| sdfhbdf wrote:
| Congrats on launching :) How does it compare to React Context
| that is built-in?
| aenero wrote:
| 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.
| jeswin wrote:
| > 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?
| aenero wrote:
| 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.
| jeswin wrote:
| 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.
| aenero wrote:
| 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.
| sdfhbdf wrote:
| 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.
| jhunter1016 wrote:
| 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.
| capableweb wrote:
| > 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.
| aenero wrote:
| 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!
| [deleted]
| christophilus wrote:
| Looks kind of similar to something Mithril does (or used to
| do). Ergonomically, I prefer `counter(0)` to `counter.set(0)`.
| brainless wrote:
| 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 :)
| aenero wrote:
| You'd be happy to know those are supported and documented at
| https://simpler-state.js.org
|
| Specifically, look at these "Recipes" pages:
|
| https://simpler-state.js.org/recipe-async.html
|
| https://simpler-state.js.org/recipe-orchestrators.html
| xixixao wrote:
| 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.
| aenero wrote:
| 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.
| revskill wrote:
| 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.
| [deleted]
| rajangdavis wrote:
| 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?
| aenero wrote:
| 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.
| rajangdavis wrote:
| 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.
| aenero wrote:
| 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.
___________________________________________________________________
(page generated 2021-03-22 23:03 UTC)