Hacker News new | past | comments | ask | show | jobs | submit login
React-copy-write: immutable React state management library (github.com)
57 points by xrd 6 months ago | hide | past | web | favorite | 25 comments

What is it about React, that needs you to use a state management library? I have programmed in Servlets, JSP Model 2, ASP.NET, iOS, Android, Windows. None of these require a state management library. But React developers seem to think a state management library is a must-have. Is this a cultural thing? Is it due to some limitation of React?

You absolutely do not need to use a state management library to build a React app, and, in fact, you need them even less now than you did a couple years back, because the Context API got so much better.

For a lot of straightforward React applications, state management libraries probably make things worse.

"I have programmed in Servlets, JSP Model 2, ASP.NET, iOS, Android, Windows". I suspect in those kind of applications your state management library is just your database. In lack of a database and having the need of reacting on data changes, single-page applications typically resort to some kind of state management library.

In fact, with the Meteor framework state management largely revolves around a client-side reactive mongo API on top of your data, which IMO gives me the best developer experience.

The main issue is that React needs state passed around as props otherwise, which becomes unweieldy if you didn’t have a global state.

I believe it also makes debugging state issues easier if you have a single source of truth.

Edit: this is somewhat alleviated by the introduction of the React Context API[1], which provides some cleaner functionality but requires quite a few extra components in the render tree.

[1]: https://reactjs.org/docs/context.html

>> state passed around as props

Context provides a way to pass data through the component tree without having to pass props down manually at every level. https://reactjs.org/docs/context.html

>> single source of truth

Often, several components need to reflect the same changing data. We recommend lifting the shared state up to their closest common ancestor. https://reactjs.org/docs/lifting-state-up.html

Note that Context is a pretty new API (1), way newer than most state management libraries. Before that it quickly became unmanageable to lift state up and pass props down all the way.

[1] https://github.com/facebook/react/blob/master/CHANGELOG.md#1...

I guess then I have no idea why Redux is so widely used, given now the Context API exists.

Multiple reasons.

First, Redux has been around since 2015 (and won the "Flux Wars" that had been going on for a year before that), whereas the new `createContext` API only came out a few months ago.

Second, Redux provides a lot more guidance around how you're supposed to structure your application. Context only provides a method for making values available to deeply nested components in the tree.

Third, the Redux ecosystem is huge, with addons available for effectively any use case you can think of. [0]

Please see my post "Redux - Not Dead Yet!" [1] and Dave Ceddia's post "Redux vs the React Context API" [2] for more details on how they relate.

[0] https://blog.isquaredsoftware.com/2017/09/presentation-might...

[1] https://blog.isquaredsoftware.com/2018/03/redux-not-dead-yet...

[2] https://daveceddia.com/context-api-vs-redux/

Redux is overused, because of a meme that suggests that the de facto standard React "stack" is React, Redux, and React-Router. That's just a meme (Dan Abramov even wrote a blog post debunking it!) but some memes are hard to fight.

Redux the library is not "state management for react". It's more of a concept that gives you a predictable structure around how state changes over time.

I think what you're thinking of is specifically the `react-redux` library which gave a nice abstraction to the old & clunky React context API. The new context API was released with React 16.3 (which came out March of this year).

The new Context API can't be used in lifecycle methods, which is a bit of a letdown.

the new Context API replaces the `connect()`/`<Provider />` functionality, but not the flux data flow model. Nor does it provide a replacement for the surrounding redux ecosystem - so there's still plenty of benefit to using redux.

> I have programmed in Servlets, JSP Model 2, ASP.NET, iOS, Android, Windows. None of these require a state management library.

All of these have State Management involved somewhere, you might not "obviously" have a "State Management Library" because your stack might encode it simply as "best practices" or it might be a low level detail in the stack that you treat as a simple, black box, but more often than not in many of those environments you are building ad hoc state management libraries all over the place, each application its own little snowflake of state management.

As someone else pointed out, you might be strongly relying on a database or an ORM as your primary state management library. Most React projects are using In Memory data structures, for a vast majority of their work, and most state management libraries you can think of as simply an "ORM-like" thing for managing CRUD in memory.

But we can push the abstraction further. The ORM is often used to build the "M" in an MVC pattern. There are all sorts of complicated Model patterns in various MVC frameworks. Some have more boilerplate than others, some have complex engines and libraries that do a lot of management work. A lot of models use "plain old class objects" (POCOs) of one sort or another, but then have all sorts of ravioli code inside those classes to maintain that the state follows business logic rules, and notifies other classes of necessary changes, etc. That of course depends somewhat on the separation of concerns between your Models and Controllers. (I've seen a lot of "MVC frameworks" and "MVC apps", and almost no two are alike in their M/C separation choices, or their overall state management architecture.)

React itself solely concerns itself with View as a framework; it leaves Models/Controllers entirely to the user. Various React state management libraries are different variations on Model/Controller logic/preferences.

(Some React state management libraries more explicitly define the Model/Controller separation and powers than just about any MVC framework for the platforms listed above.)

As you get into more recent, more "reactive", patterns you see a lot of movement toward "MVVM" Model-View-ViewModel. In those patterns Views and ViewModels are a bit more tightly coupled than MVC. Often a VM has a way to signal data changes (for "two-way" binding) to the View so it can update just the things that changed. In .NET the pattern is called INotifyPropertyChanged. Most other JS frameworks right now (Angular, Vue, Aurelia) follow similar patterns to MVVM, somewhat tightly coupling Views to ViewModels in a similar way to MVVM, even if the frameworks often obscure those details in proxy/signal "automagic". If you look at MVVM projects you will also find some variety for how data changes are signaled. Even .NET's INotifyPropertyChanged has lead to a variety of different toolkits and MVVM libraries designed to manage it and orchestrate it.

React instead only "allows" one-way data binding in views. So even from an MVVM standpoint as opposed to an MVC standpoint, this leaves React state management libraries with obvious goals to do some of the INotifyPropertyChanged-type actions of knowing which state changes at a time, giving React a leg-up in its "one-way" data binding to better know when to re-render components based on state changes. (In many cases a benefit here is that most React state management libraries don't necessarily return to tightly-coupling Views and ViewModel equivalents, most of them look more like MVC than MVVM, despite something of a closer comparison to MVVM in desired outcomes.)

Most of the individual state management concepts in React state management libraries exist in other frameworks, they just often have other names (ORMs, Model Libraries, Controller Patterns, MVVM Frameworks, etc) or are even considered as "user" parts of the stack, left to the individual project to build ad hoc as needed.

Part of what React's state management diaspora has been good for is questioning some basic assumptions in MVC and MVVM patterns. One of those assumptions is "mutable state" where you might just directly update POCO objects each time state changes. (Which works, and has been used for a long time, but also has a history of interesting bugs and debugging issues.) The library shown here is interesting because it's one trying to get the programming benefits of immutable state with the "POCO" terseness of mutable state (`draftState.itemList[55].name = 'new name'` instead of something more like `nextState = curState.setIn(['itemList', 55, 'name'], 'new name')`).

I haven't seen any libraries using the new context API make use of the observedBits prop (currently called `unstable_observedBits`, as its not officially supported). There's a comment in this library's codebase that hints that it will be used in the future. I wrote a bit about this API bitmask-based API a few months ago while I was experimenting with it[1], so I'm curious about its current state and whether others have experimented with it or know about its future in general.

[1] https://hph.is/coding/bitmasks-react-context

I'm _specifically_ hoping to use it in React-Redux v6.x, and wrote up a very extended description a couple days ago of why and how I hope to use it:


I actually referenced your blog post as a good overview of how to use the bitmask API, so thanks for writing it!

I also filed a React RFC to make the API a bit more flexible by allowing us to pass a `calculateChangedBits` function directly to a `Context.Provider` instance:


Sebastian Markbage replied in that RFC thread, and tbh the discussion seemed a bit odd. It seemed like he was discouraging me from trying to use the bitmask API, or trying to figure out why I might actually want to use it, when it seems like the entire reason it exists is exactly for something like React-Redux to leverage it for performance optimizations.

I had a few versions of react-copy-write working with `unstable_observedBits` but I couldn't decide on the right optimization strategy. At this point I'm not sure how likely it is that this API will be released as stable, so I'm going to hold off until thats more clear.

This is a nice library but state management libraries that rely on components in the render tree (function as a prop or children) mean you cannot access those properties in the immediate component's react lifecycle methods.

Instead you have to (sometimes arbitrarily) have a child component which takes in those properties. This child can then reference those properties in it's react lifecycle methods.

Fortunately this is a pretty simple problem to solve, you could introduce a higher order component which takes in your component and selectors much like redux. Infact, it seems the author beat me to it: https://twitter.com/aweary/status/1011478641320980480

React team members seem to be hinting at some sort of official "adopt" (https://github.com/pedronauck/react-adopt) api which would allow for a slightly nicer way of getting access to render prop arguments. (although you still done get access to them in lifecycle methods). In the meantime this seems pretty nice: https://github.com/Astrocoders/regenerator

I quite like this approach, but haven't had a chance to try it in production yet. It seems to be performant and convenient, the biggest cost being extra bytes.

In general, immer plugs in nicely to a lot of contexts, such as redux. Here's another example of how it works with tiny-atom: https://qubitproducts.github.io/tiny-atom/using-with-immer (disclaimer: yet another state management lib, which I wrote).

An alternative approach to immer I've been using in production is http://zaphod.surge.sh/, which is more like an immutable lodash.

You can also use a proxy object to keep state in sync between client and server. Replace verbose API calls with JS objects. Reading and writing from server become native JS mutations.

Proxy is not yet supported widely enough, sadly.

Not bad, I like that it seems like a simple wrapper to the Context API, but I'll still probably continue directly consuming the API itself that when using Apollo GraphQL

Seems like they lifted a bit of the react context api but made things even easier...

Does anyone know when these non-redux approaches are going to get something like TT debugging?

Tiny Atom integrates with Redux devtools https://qubitproducts.github.io/tiny-atom/devtools and allows jumping back to any state. It also:

* uses the new React.createContext

* is [highly optimised](https://medium.com/@kidkarolis/tiny-atom-3-0-0-e65a50de7f4a)

* provides `<Provider>`, `<Connect>` and `connect()` for both react and preact

* can be plugged in with immer

* explores how to improve over Redux API by combining dispatching and mutations in actions

* is small

Thank you, will check it out!

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact