React Common Tools and Practices: State Management Overview
react-community-tools-practices-cheatsheet.netlify.app
react-community-tools-practices-cheatsheet.netlify.app
When I was learning React, I kept putting off Redux because youtube said it was complicated and you didn't need it after useContext. Big mistake. Our app became more complicated because our state management(useState/useContext) was so simple. Switching to Redux dramatically simplified our most complicated components.
Also, the Redux dev tools extension is a game changer. It lets you see every state change, in order, with traces and diffs.
https://github.com/zalmoxisus/redux-devtools-extension
Pros can obviously make up their own mind. I’ve tried Mobx and the tooling just wasn’t there, but I hear their diffing algos are good.
All that said, this new "React Community Tools" site isn't intended to specifically plug Redux or recommend it instead of other tools. I want it to be a catalog and informational site that helps people understand what tools are out there and what problems they solve, so that they can make decisions about what tools to use based on their own needs and applications. I just happened to write a Redux page first because that's what I know about, and I wanted at least one draft page up there so that we can nail down what some of the content structure for these pages should look like and use that as a basis for getting people to contribute pages for other tools.
[0] https://redux.js.org/tutorials/essentials/part-2-app-structu...
[1] https://redux.js.org/tutorials/fundamentals/part-8-modern-re...
Most important for beginners is to pick one state management solution and learn it quickly. Don’t waste time touring all of the options or trying everything. Pick one, learn it well, and get it going. You can evaluate alternatives after you understand the basics of one system.
- Writing immutable update logic by hand is really hard to do correctly and hard to read
- Accidental mutations have historically been the number one cause of bugs in Redux apps
Immer eliminates accidental mutations, and makes it _way_ easier to write and read code that does correct immutable updates.
So, we now recommend Immer as the right way to do immutable updates in Redux [0], and Redux Toolkit uses it by default [1].
Agreed that there is absolutely a learning curve aspect here - you need to know why and how to write immutable updates by hand to really appreciate what Immer is doing for you, which is why I've repeatedly highlighted that information in our docs [2].
[0] https://redux.js.org/style-guide/style-guide#use-immer-for-w...
[1] https://redux.js.org/tutorials/fundamentals/part-8-modern-re...
A deep clone would result in unnecessarily copying a bunch of extra objects and arrays that aren't relevant, so that's a waste of cycles. On top of that, the React ecosystem (and React-Redux in particular) relies on reference comparisons to determine if data has changed. If you copy objects that didn't actually get updated, it can cause a lot of components to re-render when they really shouldn't have.
For more details see:
- Dave Ceddia's "Complete Guide to Immutability in React and Redux": https://daveceddia.com/react-redux-immutability-guide/
- The Redux docs page on "Immutable Update Patterns": https://redux.js.org/recipes/structuring-reducers/immutable-...
- The Redux FAQ entry on deep cloning: https://redux.js.org/faq/performance#do-i-have-to-deep-clone...
- The Redux FAQ page on immutability: https://redux.js.org/faq/immutable-data
- Bad: newX = JSON.parse(JSON.stringify(x))
- Slightly Better: newX = lodash.cloneDeep(x)
- Best(using immer): newX = produce(x, draftX => {draftX[1].done = true})
I don't know how JavaScript engines actually assign memory, but I think of object references in JavaScript like pointers in C. If you deep clone you're copying way more than you need, mallocing giant blocks of memory for duplicated data. Libraries like immer(the best) and immutable.js will only copy what you changed, which saves time and memory.
React noobs should just stick to React's own useState and useContext hooks. There are issues with it, but it's not as bad.
Here's what Pete Hunt from the original React team said last week: "Literally never ever use Redux on a new project under any circumstances. Even if it’s what the team knows." [1]
1: https://twitter.com/floydophone/status/1365824299060273153?s...
- The React ecosystem has fragmentation because there's many ways to do things, and he feels someone needs to start narrowing down suggestions to reduce confusion.
- There was an article going around on "Why I'll Never Use React for Enterprise Apps Again", and half of the problems listed in the article were about Redux and React ecosystem related issues instead of React itself, so Pete's upset about that
- Pete feels opinionated data fetching tools and explicit state machines solve some use cases better
- Pete thinks it's too hard to dynamically code-split Redux logic
So, those are valid technical points that can be discussed in detail. But those are also a very far cry from "NEVER EVER USE REDUX!", and I'm really not sure how you jump from "one article said they had trouble migrating their code from `connect` to hooks and they aren't happy with Redux-Saga" to "NEVER USE REDUX" either.
I actually pointed out that we have a new upcoming "RTK Query" API that's in alpha right now [2] that drastically simplifies data fetching and caching for Redux, and Pete said later in that thread he thought it looked impressive. He also said he appreciates the work put into Redux Toolkit and our docs updates.
So yeah, Pete said that, but as usual there's a lot more nuance in the later discussion that should be highlighted rather than the clickbait-y initial tweet.
[0] https://twitter.com/acemarke/status/1365824897210064897
[1] https://betterprogramming.pub/i-almost-got-fired-for-choosin...
Learning the built-in React features goes a longer way than specific state management libraries if you're starting out. I'm really wary of anyone selling a specific set of libraries as being the way for beginners to go, unless they're tackling apps that have very specific state management needs.
That's also the kind of advice I want to have in this new "Community Tools" resource site. At the same time, this site _is_ intended to list common tools in use in the ecosystem and when it _might_ make sense to use them.
As other comments have mentioned, he has valid concerns and some opinions about what a preferable architecture looks like but for whatever reason he chose not to lead with that.
It's a nice pattern but in an artistic sense.
Changing UI state 10 years back:
state.totals = 100;
Changing state today:
1. Call an action creator
2. Dispatch an action to the reducer
3. Switch by action type
4. Apply action on state
5. Return new state
So imho the problems start right at the bottom - the extreme verbosity and the redundancy.
Also, two-way-data-bindings like just `state.totals` work when you're just re-rendering your whole app on every frame, but as soon as you get away from that, there will always be either some "observing" or "signalling" involved to trigger rerendering of the correct parts of your application. Redux goes the "signalling" way, MobX goes the "observing" way, both have their benefits and drawbacks.
There's no need to rerender the whole frame - will be happy to provide an example on request.
The upside of an action-based (signalling) approach is that you have a better time separating concerns as actions (if used correctly) represent intent, so your component emits an event and the state acts accordingly, removing state logic from your representational components. Might also be a lot easier to see in the DevTools what happened when and why.
Of course not every app needs a pattern like that, that's why I said both approaches are valid.
Regarding DevTools, it's just as easy to make it work with "observing".
Also, I'm not saying you should write state changes and data fetching in the UI components. By all means write them under actions/services whatever. I'm complaining about the complexity around effecting that change.
It's not much more code than writing a function, but other reducers of your state can suddenly act upon the same action, you can trigger side effects using middleware and of course watch stuff "by intent" and not "there was a modification" in the devtools. All while keeping everything separated pretty well.
--- start quote ---
import { combineReducers } from 'redux'
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
const composedEnhancer = composeWithDevTools(applyMiddleware(thunkMiddleware))
import { createSlice } from '@reduxjs/toolkit'
...createSlice will automatically generate action creators
--- end quote ---
It's.... it's not better in any way?
> Redux goes the "signalling" way, MobX goes the "observing" way, both have their benefits and drawbacks.
I like Svelte's approach. Where they figure out which things are being observed or changed at compilation/transpilation time and generate code for that case specifically.
In the end you are left with only
`import { configureStore, createSlice } from '@reduxjs/toolkit'`
Perhaps. As person who's never used thunks or, really, half of those tools in conjunction with Redux, this tutorial is quite confusing.
> In the end you are left with only import { configureStore, createSlice } from '@reduxjs/toolkit'
The final code on the page is literally
import {
createSlice,
createSelector,
createAsyncThunk,
createEntityAdapter
} from '@reduxjs/toolkit'
The page doesn't really address the issue of verbosity and the flow as described in the original comment. It just hides some of it behind some facades with autogenerated accessor functions (so, even more levels of abstractions).- You've gone through this whole tutorial sequence and should now understand how to write all this Redux logic "by hand" and why these standard Redux usage patterns exist
- Now, here's how Redux Toolkit abstracts and simplifies those exact same patterns, so that you can do the same thing with less code, but you understand what those abstractions are doing for you internally.
Most people take umbridge because Redux was cargo culted in the first place, cargo culting against it isn't that helpful. I think the website shared in this post is a great approach. Show everyone the options and let them decide what solution fits their problem.
Also, with action factory, step 1 can be automated.
https://redux.js.org/tutorials/fundamentals/part-8-modern-re...
It doesn't eliminate the "dispatch actions" concept, because that's core to how Redux works. But, it does simplify the vast majority of your Redux logic considerably.
In general, if we use any async APIs in your JS that modify shared state the order will consist of an interleaving of all events, including from timers (macrotasks), DOM events (macrotasks), resolved and finalized promises (microtasks) and DOM APIs like IntersectionObserver (microtask), IDB et al, along with synchronous calls (including any recursive calls) so looking at things in order does not mean anything in a complex UI with lots of interleaved events if you cannot filter by a named action (the action that caused a certain chain of events, which otherwise will be displayed intermingled with all other events from other actions) you can't really follow the data and control flow of your app. So order only matters in debugging and comprehension if you can filter by action. Another thing we should care about is domain driven design and domain aggregates for your state store to make sure that only one aggregate root is responsible for a given part of state. If more than one chain of events (more than one named action) modify the same part of state then trying to comprehend the relationship between state and action becomes too complicated and you lose consistency guarantee as a result (overlapping and interleaving reads and writes) unless you introduce some form of MVCC (multi-version concurrency control)
I've built tools to visualize how complex highly concurrent UIs work under the hood and found the principles and methods to be directly applicable to analysis of microservices based apps.
I also have to imagine the amount of boilerplate you write to use Redux + Immer is orders of magnitude over mobx, but maybe some people prefer it to be slightly more explicit
However, that process is very tedious and error-prone due to writing lots of nested spread operations.
Immer effectively does those same steps, but automatically. This is all completely different than how Mobx works.
Immer has a slight performance overhead [1], but in practice reducer logic is almost never a meaningful perf bottleneck in a React+Redux app - the cost of updating the UI is might higher.
It's also worth noting that Immer was actually created by Michel Weststrate, the author of Mobx :)
[0] https://redux-toolkit.js.org/usage/immer-reducers#basics-of-...
Not a surprise to me that mwestrate is the author after seeing the description of Immer - he has an excellent graph knowledge and applies clever architectures & optimizations from that.
I recognize it's completely different from how MobX works, I simply mean to say that I feel MobX is superior and this mere idea of mutability / copying instead of smartly updating a graph and correspondingly only the observer portions of your UI seems like the core of that
Kent C. Dodds has a great article covering both topics:
https://kentcdodds.com/blog/application-state-management-wit...
You are right, if all your state is "cache state", then you don't need Redux. But at that point you don't need any state management library and are already searching for the wrong tool from the beginning. If you have actual global state, you'll come very quickly to a point where an actual state management library beats writing your own though, both in developer experience as well as performance.
Which you don't have to. React has been shipping useState, useContext, useReducer hooks for quite some time now - and will get you the exact same patterns seen in Redux (if so inclined).
- https://blog.isquaredsoftware.com/2021/01/context-redux-diff...
- https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-...
Now if we're talking very high frequency updates, you'd probably keep the state in the component. That'd be faster then either approach - and such components are likely few in number.
Agreed, that's why I wrote "realizing" and not "having". Some devs use Redux as a hammer when they actually need a screwdriver - they just don't know better.
- Common tools for making AJAX calls (`fetch`, `axios`, self-written wrappers around `fetch`, and when to use any of those)
- Info on things like REST and GraphQL as common backend API types
- Libraries like React Query, SWR, Apollo, and Urql as common data fetching abstractions, and their use cases
Tanner Linsley, author of React Query, has already said that he's happy to write a page on when it makes sense to use React Query [0]
I suppose we'd probably want to end up having info on things like Firebase and Hasura too - maybe those would be better suited for a "Backends" section? Like I said in my sibling comment, right now I'm just trying to kickstart the idea of this site, get it off the ground, and get others in the community involved in making it a reality :)
[0] https://twitter.com/tannerlinsley/status/1368600315432468482
Speaking of redux, I'm a huge fan but React ships with a useReducer hook now - thanks in no small part, I'm sure, to the author of redux working on the react core team. These days, I'm all about reactQuery + react's native useReducer via the hooks and context api. described beautifully by Kent Dodds here: https://kentcdodds.com/blog/how-to-use-react-context-effecti...
I feel like these state management libraries have largely served their purpose in establishing some agreed upon conventions that are now just baked in to React and other libs.
For background, I've had to repeatedly spend time answering questions all over that really boil down to people not knowing what problems specific tools solve and when it _might_ make sense to use those tools (like, say, "Redux vs Context" [0]).
The React team has kept the React docs and their advice focused on just using React itself, and avoided making recommendations or documenting tools and practices in the ecosystem. While they are in the middle of rewriting the React docs from scratch [1], as far as I know the new React docs still won't try to address things like "here's common tools in the ecosystem".
So, I decided it was time to do something about this [2]. My goal is that this would be a moderately opinionated and curated site where people can see a list of tools and practices in various categories like "state management", "styling", "build tools", and so on, and read descriptions of "what is this tool, what problems does it solve, and when should I consider using it". I _don't_ want to turn it into arguments over "TOOL X IS BETTER THAN TOOL Y!". I _do_ want to get experts from the community involved in writing these pages, like having the maintainers of libraries like Mobx and XState write pages on when their own libraries should be used, as well as when it makes sense to use React component state instead.
Yesterday I sat down for a few hours and hammered out a quick draft of the first couple pages for the site - this "State Management Overview" page, and a description of Redux and its use cases [3].
This is absolutely a first draft, and I hope to get the rest of the community involved in building this out! I just wanted to get _something_ written and live as a starting point for discussion and to kickstart the project.
*edit*
Okay, seriously, let me reiterate:
THIS IS A FIRST DRAFT THAT I JUST THREW TOGETHER IN A FEW HOURS.
I haven't even had a chance to go back and add more material to this "State Management Overview" page, like some of the info and resources I dug up yesterday [4].
That said, I _do_ want to get the React community involved in fleshing out the categories and material on this site!
[0] https://blog.isquaredsoftware.com/2021/01/context-redux-diff...
[1] https://github.com/reactjs/reactjs.org/issues/3308
[2] https://github.com/markerikson/react-community-tools-practic...
[3] https://react-community-tools-practices-cheatsheet.netlify.a...
* kSQL' EMIT CHANGES
* Spark has stream-stream JOINs
* Flink's default behavior ('time-varying relations') is to emit the changes
* Materialize's value proposition is centered around event-streaming, and has Live Materialized Views.
, is there a need for a client-side reactive SQL lib (react plugin?) where reactive data management would be done on continuous queries?
If your client data is only a slice (CREATE VIEW slices AS SELECT .. JOIN .. ON .. = u.user_id [WHERE ..][GROUP BY ..]) of your server data, wouldn't it:
* help a lot in cache management (as in: none to do. If you get reactive updates on changed data you don't need a cache)
* help in moving client-side logic server-side, and vice versa (since it would be expressed in the same language)
I mean: is there a market for it? Some people really do like SQL, I know I do.
Here’s an example where I refactored a complex usereducer+context to xstate https://swizec.com/blog/react-context-without-context-using-...
I'm not using it yet, but playing around with it did convince me that thinking in state machines or state charts is very helpful the moment the UI gets a bit more complex.