5 hours before this HN post I asked for comparisons with different site management libraries on Github here: https://github.com/keajs/kea/issues/106
React state management is indeed a mess, meaning there are so many options to choose from. Unfortunately, even for me, a library author, it's hard to evaluate all of them properly. It would take just too much time and you never know where the real problems in any framework lay before you get neck-deep in code. And then you're too far to switch to another framework anyway.
Someone should really map out this landscape in more depth than just an awesome list. I hope the issue linked above can spark some discussion.
In any case, I personally think Kea is a very good solution for React state management, yet obviously I'm biased. Shrug.
And, to keep it on the topic, Recoil seems very low level and I think you need to invent a lot of things yourself to get a full app out of it. Cool to see people trying stuff though :).
And it's from Facebook so who knows, just by proxy it might become the default "atom" and someone will build molecules out of them...
I use to use just Redux and it was such a pain.
I know you have added even more features in v2 that I will need to take advantage of.
If you need to add examples https://versoly.com/ is what I built on top of keajs.
Has about 8 logic stores that have 100s of actions, thunks, reducers and some interesting selectors.
Would you mind posting in this "who's using kea" issue about versoly as well?
"Unfortunately, even for me, a library author, it's hard to evaluate all of them properly. It would take just too much time and you never know where the real problems in any framework lay before you get neck-deep in code. And then you're too far to switch to another framework anyway."
Putting that aside, to me RxJS and especially Cycle.js are "1 abstraction level above" React & friends. (Cycle can be used via React.)
What do you think about these?
Frankly, for state management I still haven't found anything that beats Redux on its own without thunks or sagas or any of that bullshit. This is despite doing my level best to see if useReducer on its own would be sufficient. It's not.
Thunks are unmaintainable. Sagas are put together with bailing twine and rely too much on generators for my taste. (The source code is smart but it's also wildly complex.) All of it is castles in the sky for no good reason. You don't need any of that anyway.
useReducer is equivalent to useState in that it works on a particular component and all of its descendants, rather than being orthogonal to the React tree.
I think if you can model something with pure functions, you should. That's the approach we try to take for asynchronous processes: just a pure function that you happen to evaluate on some other process. This obviates the need for things like sagas. So I agree with you there I guess.
If you post an example of what you don't think it could handle I will tell you how we would handle it.
I'd love to take a look at a larger project using Recoil though, just to get a sense for how it looks with a relatively complex state setup. My first impression is that it would get messy pretty quick, but I've been wrong many times before :)
Also, I'm not trying to shit all over your project, congrats on rethinking state management. Regardless of how I feel about your library, that's still awesome.
Don't I get the same perf with selectors?
There was a period in the early days of Redux where MobX [0] (and, perhaps to a lesser extend, MobX State Tree [1]) was the main competitor to Redux that I used to hear about. They both seem to be actively developed, but I don't hear so much about them any more. Have you ever look at either of them?
BTW I am 100% with you on Sagas.
[0] MobX https://mobx.js.org/README.html#introduction [1] MobX State Tree: https://mobx-state-tree.js.org/intro/philosophy
Only problem so far, or inconvenience maybe, is the sometimes complicated ways you come to intertwine stores which have access to different parts of the fetched API data. So if you need data Y stored in store X inside store Z, you have to inject store X as dependency to store Z. Getting too carried way with these injections can make your whole state quite messy. Or loading too much logic into one store.
Probably that's just an architectural issue, that can be remedied with enough thought put into it, but I haven't at least found a clear pattern that would make it simple to decide when to break up stores, or how to refactor them.
Also you have be aware of the complexities of observables which are a bit magical. Knowing when observables don't update and how to observe in general is very much a necessity (unless you want interesting bugs).
I have been curious to try out Mobx State Tree but alas haven't had time.
Also, sign me up to the Sagas-are-bullshit club.
Why is everyone so eager to add third party state libraries from the getgo? In most projects, Redux doesn't add net value.
For me, Redux works very well with how I think about control flow. That said, my brain has been ruined by a youth misspent in fp land, so my preferences should be taken with a liberal application of salt.
The library has been around for a while and reduces a lot of boilerplate.
Is also used by some quite big names.
Still a long list of chores to improve API, tooling, etc but very performant and battle tested.
* They're a pain in the ass to maintain once you have more than a few async calls
* They are a poor abstraction because async messages often need to do a lot more than just call/response/error. Sometimes you want stuff like backpressure, throttling, etc. and that's a lot messier to model with a thunk
* There's no real need for a library for thunks in the first case, you can mimic the same functionality with the same amount of code by putting async calls in mapDispatch
If I'm working on a small project then shoving async in mapDispatch can work. Most of the time though the best approach is to handle async through a custom middleware pipeline because let's be real–if your project is simple enough that thunks are fine, you probably don't need to be doing it in React.
Still don't like the extra naming that you have to think of for every atom/value. Or also the key: prop that you have to define for every atom. It seems redundant for me.
And also, as a bundle-phobia guy I am. I think 22kb as extra library is ok but it could be better.
Liking a lot Recoil as solution for the three-button problem. I still think that the patch pattern I use with dop is better. In terms of simplicity and extra KB. This is the same example https://codesandbox.io/s/react-dop-nhj0j?file=/store.js
But I guess I say that because I'm the creator of dop. So I'm so open to critics :)
Consider iOS for example. Just add fields in App and you're done: https://developer.apple.com/documentation/swift/maintaining_...
Similarly in ASP.NET: https://docs.microsoft.com/en-us/previous-versions/aspnet/75...
Same in JSP: https://javabeginnerstutorial.com/jsp-tutorial/state-managem...
But in React it is such a huge issue, and there are multiple competing solutions. There must be something wrong with the design of React that is causing this.
I would say React's state management is its great success, rather than its `functionalness` or its reactivity. No other UI framework (QT|WinForms|MOTIF|GTK) really ever handled state well.
The main "Apple approved" tools for user interface and state management are CoreData and UIKit. Both are imperative, mutable, and overall difficult to work with. This might be subjective, but I always notice just how poorly Apple's own apps work. There is always some button that is stuck in the wrong state, some feature that doesn't do what its supposed to, and some broken animation when views reload (I'm looking at you Podcast app). I can't know why, but my hunch is that Apple engineers are forced to use unwieldy patterns and technologies like CoreData, MVC, and UIKit, and are drowning in a sea of unmaintainable code.
As a response to the inadequate tools Apple provides, big industry leaders have stepped up and rolled their own declarative solutions. IGListKit from Instagram is a prime example of a declarative user interface solution thats vastly superior to Apple's. Going even further, Facebook basically ported React into iOS with ComponentKit.
It took Apple a good decade, but they are finally giving us these types of tools as well with SwiftUI. However, judging from its low adoption, so far they have not managed to fully get things right.
First, in most frameworks there is only one place to keep your data. This can be your App object or session state.
Having view automatically refresh is overrated. Given the amount of work you have to do (actions, reducers, dispatch and whatnot) it is easier to just update the view yourself.
I'm not sure it's a hassle, just different schools of thought.
> This is not the case in other frameworks.
None of the examples you mention are reactive AFAIK. The point of React is that the UI is a function of the state. How exactly that is accomplished is where those different schools of thought come into the picture.
Here's one such lib: https://www.npmjs.com/package/mvc-router-spa
FWIW I've never seen a really great solution for this in any JavaScript framework. I read it as a sign of React's maturity that this is a focus.
But Recoil is slightly different, designed to be “component first” essentially. Not global. It’s how my own personal mobx based library works. But this one likely has concurrent mode baked in.
Re-frame wraps Reagent, and introduces the concept of "subscriptions". A subscription either returns the content of an atom, or state derived from it, equivalent to Recoil's selectors. Re-frame also introduces the concept of a global application DB, a single atom that contains various pieces of state, such that you can develop your entire app around it.
I haven't tried Recoil, but I'll give it a shot on my next JS project - I tend to use ClojureScript for front-end precisely because I find the Reagent/Reframe approach simpler and more effective than any of the plain JS React approaches (for complex apps, anyway).
Also is there any support for getting the values of atoms outside of a hook? For example from an async action that is not coupled to the render loop which also wants to know the current value.
In other words, this updates both of the atoms together:
const [a, setA] = useRecoilState(atomA);
const [b, setB] = useRecoilState(atomB);
...
onClick={() => {
setA(a => a + 1);
setB(b => b + 1);
}}
If the new values of multiple atoms are interdependent on each others' current values, it's possible to update multiple atoms together using the transactional/updater/function form, but we need to add a nicer interface for doing this. Coming soon! The nicer interface would probably look something like this: const update = useTransactionalUpdate([atomA, atomB]);
...
onClick={() => {
update(([a, b]) => [a + b, b - a]);
}}
It's then easy to make a hook that provides a reducer-style interface over a set of atoms. But now, unlike with Redux or useReducer, each atom still has its own individual subscriptions!