Principal eng working with 3 teams on a ~500k loc React site here. After lots of experimentation, we’re just using React contexts.
- Redux is cool but there’s so much decoupling that it gets hard to read the code. New hires would take too long to start, TS types got crazy, it was too much.
- XState is very good for state machines… but you barely ever need that level of control. Most of the time understanding it ends up being overhead. We’ve had maybe one good case for using it in about 10 years.
- React’s contexts are minimum overhead and work very well with “jump to definition” functionality and TS types. You can choose to use a reducer if you want, or skip it if it isn’t appropriate.
I came into a fairly complex app using redux. I asked the developer about why contexts wouldn’t be a better fit, and within a week we’d dropped redux for contexts. There are certainly uses where Redux really is the right tool, but it’s nice how flexible the built in stuff is.
Sure, but as a senior I could get juniors up to speed on redux mostly within a day, the longest was 3 days. It's not that difficult if you have a working codebase they need to contribute to.
I'm genuinely curious - do you have any examples of "misusing `createSlice`"?
FWIW I see folks misusing / misunderstanding Context on a near-daily basis, mostly around misunderstanding what components will re-render and when. That's part of what led me to write my "Guide to React Rendering Behavior" post, to clarify when/why/how React renders:
A common issue with "createSlice" even with seasoned developers is they're introducing useless nesting and weird imperative-functional constructs because they fundamentally do not understand how "immer" works.
I've read your blog post, and I might be missing something, but you do not seem to be highlighting any re-rendering issues specific to context. Juniors will have to learn how React works anyway. Context or not.
- Thinking that if you change a field in the context value only components that read that field will re-render
- Thinking that _only_ the components that consume the context will re-render, not realizing that normal recursive rendering behavior resumes from there
- Not realizing that updating a context requires a `setState` call in the parent that renders the `<MyContext.Provider>`, and you can easily end up in a situation where the _entire_ component tree renders just because of default recursive rendering
Can you point to some examples of the "useless nesting" and "weird constructs" you're referring to? I'm curious to see what's happening, and if there's any docs improvements we can add to help provide guidance.
These "misunderstandings about context" are not specific to context. React developers will have to learn about them whether they are using context or not.
> Can you point to some examples of the "useless nesting" and "weird constructs" you're referring to?
Example: Create a slice with a list of items as initial state and truncate it in a reducer.
Moved to context in full also but I will say Redux's toolkit thing they released is a nice addition that was much needed. I do enjoy using some RxJS still for certain problems. Agreed on XState. Want to use it but its way overdoing it often.
Yep when I was rewriting the MVP at my current place I looked into them all and decided that context + hooks (and a server side pass for first paint) was all we needed. It was hairy at first keeping things in store etc but we wrote some lib functions that we haven’t had to touch since.
Redux is nice, but it’s be nice to have a very minimal framework for adding a global and local states via hooks and syncing their data structures to the indexed db
We use mobx and I think it’s great. The observable / computed model is really familiar from spreadsheets so it’s very easy to reason about code and computations. You always just think about your data in terms of being computed from dependencies without worrying about pushing state though the system.
We have an object graph (updated via web socket subscriptions) and then twist and turn slices of that data on the way up to the components. I hardly think about the fact that mobx is there and instead just think in terms of reshaping data for display.
i like mobx because it encourages you to separate state from presentation more than React Context does, but doesn't discourage keeping logically separate state trees separate the way Redux does.
What do you do for modifying the data that is shared across the app? It seems like you’d either have one huge context that’s very hard to manipulate, or many deeply nested small ones?
We’re using redux (with a copious amount of selectors) and it’s the only reason I have a little bit of confidence where all our data is.
I have built a lot of apps, including huge react apps. The problem you are describing is usually a result of people representing too much in state of as part of the same state.
Now I could be wetting but it's hard to have these sorts of discussions on the abstract.
You can have many small states within a larger context, and each consumer component can choose which states it wants from the context. Basically it's a bunch of global variables.
Right but every time that context changes it triggers a re-render of every child component regardless of if it’s consuming that bit of state or not. These comments are making me really question other peoples’ understanding of what I thought were kind of basic principles of how react, context, or redux work…
Agreed. For large apps with even a little bit of complexity, context doesn't seem like much of a solution. You either have a million contexts for every shared state slice to optimize re-renders or you have a single context that takes a huge hit on performance. A single large context object is redux except redux knows how to intelligently update components based on what changed.
I don't get the hesitation to use redux. I've used it on every single FE app I've worked on and never regretted it. I can make redux do exactly what I want it to do and with the power we get from memoization of selectors via reselect, there really is nothing that beats it.
I'm also convinced that most people praising react-query are hugely missing out on the level of control you get with redux. Further, coupling side-effects -- e.g. fetching data from api -- with react component lifecycles is wrought with awkward edge-cases that you don't have to deal with in redux.
The Redux system is really hard to trace through in an IDE. It turns every simple function into lines of boilerplate spread across multiple files, and simple business logic becomes so much harder to read.
Maybe I'm just not smart enough, but I've been working with Redux for a few months and I get more confused every time I look at it, despite hours of videos and documentation and tutorials. It's just not at all clear to me what it's doing behind the scenes and how the typings get transfered and what's happening during async operations.
I don't think I'd ever want to use Redux again after this experience. I'd much rather deal with performance problems than the layers of unreadable abstractions that Redux uses...
Like most systems, it’s all about how it is setup. I’ve learned over 5-7 years how to organize FE (with redux) code so it is readable. Also inheriting a system can be quite the burden and I could see how it was be difficult to grok.
Maybe that's where part of your confusion lies, the redux flow itself is completely synchronous. I've seen people write async dispatch(), then getting confused as to why it's not taking effect.
Probably writing lots of state machines in C helped. Redux at its core is basically just a state machine. Through functions (actions) you invoke the reducers which change the state. And then you use that state in your app.
I do agree that the documentation around redux is not very clear at the minute.
And I will add that the best tool is the one you understand the best.
Just to check, are you using "modern Redux" with Redux Toolkit + React-Redux hooks, or the older-style legacy "handwritten" Redux patterns that existed before Redux Toolkit?
If you haven't seen RTK, we designed it to make Redux a lot simpler to learn and use:
The same is true in redux, every action calls every reducer, every connected component renders. Thats why with and without redux you need performance optimizations like memoizing components and/or selectors
Not that with just react contexts, you can chose to split them up instead of or in addition to employing memoization
That's not true though. Every connected component subscribed to a particular slice of state updates...not every connected component, e.g. `useSelector(appState => !!appState.someThing)` only causes a re-render of the subscribed component if the value of `!!appState.someThing` changes. Unless that's what you meant in the first place but your tone and talk of memoization seemed to imply otherwise.
It doesn't. Updating state in a component triggers the re-rendering of all its children. This is what they are referring to. You can stop that propagating effect as usual by wrapping direct descendants in React.memo().
Be aware, that an anonymous list of components will show up as "re-rendering" in the browser tools. You can verify that this is not(!) the case by looking at the profiler instead.
Additionally, if you cannot tolerate even just one component re-rendering, you can simply use React.useSynExternalStore() instead of Context.
My takeaway from these comments is that people either don’t care or literally don’t know how context works but are talking about how it’s better than redux lol
Context does not trigger the re-rendering of all children. Updating state does. And you prevent that propagating effect by wrapping direct descendants in React.memo().
Absolutely, and that's a major reason why we specifically teach the React-Redux hooks API as default today, and have guidance on setting up "pre-typed" hooks with the store types baked in:
In my experience most of my state in contexts changes infrequently (auth information, or the “current client” for a dashboard, or whatever), and then local state is sufficient for the rest. The few times I have sufficiently complexity in the “middle” I indeed have been annoyed and had to be careful to avoid over-frequent rerenders.
It’s just rare enough that “prefer contexts” is a good rule. Simpler, easier to understand code, with no Redux nonsense.
React Router moved to contexts some number of versions ago...as I look into libraries for React, I see most leveraging Contexts. I've had success with it over the past year and appreciate your affirmation
I assume the idea is for changes in the data to trigger changes in the UI? If you just keep the raw data on an object you still need an additional mechanism to synchronize the data on the screen.
A lot of state either never or rarely changes, so one must wonder if you really need a lot of complexity to manage all of that.
Some state does change a lot, and for that: sure. But if you just have currently logged in user or some such (which is quite often the case): meh, it just seems like a complex way to set a global variable.
This week I spent some time removing the Vuex state management in favour of window globals and it made everything a lot simpler and the bundle size a lot smaller (the other option would be to migrate to Pinia, which is significantly smaller than Vuex, but then I'd probably have to update to the next incompatible version in 2 years, and this seemed more robust).
You could even use document.querySelectorAll() to go through all the elements on your page, and update them based off values in the state, whenever window.setState() is called. This is basically all front-end frameworks in a nutshell. You've invented reactive programming in the browser.
Perhaps my way of thinking is React-centric but this feels like a pretty inefficient way to do it. Feels a bit like reinventing the wheel poorly when things like MobX exist, sure it’s simple to start off but then you hit performance issues or issues with dependent state etc. etc. and you realise why, for all the derision they sometimes get, libraries exist to solve this problem and they are useful.
But for a simple thing built with JS that isn’t going to need to scale to a large codebase or whatever, sure, keep it simple!
I build games where I run a global setState on the root component 60 times a second and even with deeply nested components it's not even a factor in performance.
If you don’t have the overhead of React you have multiple orders of magnitude more headroom. That’s enough to push common UI idioms as far as they can go in many cases - for example, interactive tables with at least 10^5 rows perform well in modern browsers so using the DOM means you can keep things simple and fast without having to struggle with the scrolling, text selection, accessibility, etc. concerns that custom widgets entail. An awful lot of applications exist where you need more than hundreds of records but fewer than millions.
Interesting, I wonder if the overhead of updating UI based on state “efficiently” (eg MobX style tracking of what should re-render when certain data changes) would actually be slower than brute force updating everything in this scenario? A bit like I’ve heard people talking about just rendering the UI to a canvas, a whole new render 60 times a second, is actually the most efficient way to update a UI for certain cases like really big stock ticker displays (versus selectively re rendering bits of HTML)
For anything non-trivial you'd have to add some kind of listener or tag to every node, find said node, and then update (ideally in a performative way). Said updates cannot destroy existing state, but must patch it. Updates must also not break existing attached event listeners. The list goes on and on.
Things are faster than you’d expect. Tables with millions of items don’t work in any framework, but tables with a hundred items work in all of them. Since no one has really cracked the nut on performant UIs with lots of things going on, anything homegrown will be simpler and performant enough compared to the major competition.
That is one way to do it, albeit a seemingly naive way. I can’t imagine the entire page is linearly searched to update a UI element these days. Perhaps an entire component in some frameworks?
I don’t know and can’t easily find out the time complexity of things like React setState(), but would hope that for frontend code bases where there is a build step(s) and/or for typed frontend languages we could achieve better than linear (especially if N is every element on the page)
Storing state in plain objects is a nice way to go when you pick a framework that doesn't care about state management.
Forgo, Mithril, and Crank all require explicit commands to rerender, unlike React and friends, which need visibility into everything that could ever trigger a rerender so they can handle it automatically.
You'd think manual rerenders would be a big pain, but I find it a small price for allowing by state to just be regular ol' variables. It makes everything feel simpler, like I'm just using the language instead of expressing my business logic through the framework.
Mithril does full rerenders by default on event handlers and upon requests made with its request util. Normally, this would be a no-no in React land, but Mithril is fast enough that it's a non-issue, and it greatly simplifies state management (POJOs work great for global state, and closures enable a simple way to make stateful components).
Been building in HTMX for the last month and I’m loving the new paradigm. This vid did a good job of explaining the shift. https://youtu.be/LRrrxQXWdhI
(Been using preact/react since 2016, angular/jquery before that)
Adding: another reason I like it is the backend does the rendering (like server rendered react)[1] except the language doesn’t matter (agnostic) so that I’m able to use a functional language (F#) to build all my “components”.
[1]: not actually like server rendered react, since srr only happens the first page load.
Thanks for sharing this, I found this paradigm to be highly intuitive. Adding a link to the HTMX docs which also do a great job of explaining: https://htmx.org/
My personal favorite is re-frame for ClojureScript. I have used it to build things like a LaTeX editor with live preview, electron app frontends, and web-apps. There are wrappers like Kee-frame that extend re-frame by linking the URLs query to the current state, making it possible for users to easily share URLs to different states of a single page app.
The cons are:
- A fair amount of work to make changes to the state side-effect free, but the documentation has a good tutorial on how to approach this.
- It's for ClojureScript, so it's a non-starter for organizations that will not consider anything outside of the mainstream.
I have also used Elm. If I recall correctly, Elm's state management paradigm was inspired by re-frame. I found writing JSON decoders in Elm to be demoralizing and tedious. Since the creator of the language was vehemently opposed to providing any real JSON support I decided to move to ClojureScript.
It gets out of the way in terms of boilerplate, has a good solution for avoiding unnecessary re-renders, has tooling for deep state updates without messy syntax (immer) and strikes an overall good balance between powerful and easy to use. Cons might be that it is a bit more obscure so people might have to learn it. Also the learning curve is a bit steep. YMMV
Zustand is great! It is tiny and flexible (it allows you to use Flux pattern – or not, depending on your preference). It's my go-to state management solution for new React projects.
Assuming you mean a web-based application, then all state via HATEOAS, and CSS or JS in the front-end is strictly a non-essential progressive enhancement.
This is for commercial reasons. Apps that depend on assets are crippled over low-bandwidth or otherwise unreliable connections, and frequently break in the presence of aggressive content blockers. I won't do that to my customers.
It was always MobX for me, I’ve built a few sites and React Native apps with it and found it extremely productive in exchange for some magic, but it could be seen as somewhat heavyweight compared to some more “modern” solutions. Jotai and Zustand are two that I want to investigate but not used them yet.
I don't think people are concerned about the performance here. It just gets messy quickly. Most of the state management libraries allow you to avoid passing a value up and down the tree.
Amen. I'm glad I'm not the only person that has resorted to this. I've done it on Angular too in the past. Just a core service that holds all the state, import it into everything.
I have been a react dev for 7 years and have loudly advocated against state management tools all that time. React already is a state management tool, designed to eliminate the need for complex global state. You only need external state management if you don't fully commit to the paradigm.
Sorry but what? What do you do when a parent component needs access to a particular slice of state? Refactor the entire tree? What about deeply nested components? Prop drill 100 levels deep?
please no. my work does this and the events are completely untraceable. you have no idea what's listening to what or where it will be caught or prevented
Recoil. It works well, I was new to React and just use React state vars, but there's too much complexity that builds up over time.
The app I'm building (https://inventai.xyz) is non-trivial, especially the canvas. There hasn't been any cost or downside. Quick to learn and implement.
Personally I have enjoyed Pinia the most when working with Vue projects: https://pinia.vuejs.org/
It is simple (no need for lots of boilerplate code), reasonably performant, integrates with the browser Vue dev tools nicely and is just generally a pleasure to work with, especially because Vue does a few things better than React in my eyes - most notably, the hooks.
Pinia in particular also seems to generally get out of your way and doesn't enforce a particular architecture upon you. Want to add methods to the store to interact with an API and not have to think about that in your other components, merely call a method for initializing some data from the API that will take care of the store state? Sure! Want to do the opposite and only use the store for persisting data, regardless of where it's initialized? Go ahead. Want to have your components react to changes in the store? This will work with either approach.
This is subjective, of course, but personally that combination of tools has resulted in a pretty good development experience, in my eyes something like the following is a nice stack:
- Vue 3 with Composition API (hooks)
- using SFC with <script setup> (takes care of some boilerplate)
- using Pinia for state management
- using PrimeVue or a similar component library (to have ready-made components for most needs)
I've been working on a large React + Mobx codebase for nearly 3 years now, and it's still holding up surprisingly well. Mobx is really well designed and plays nicely with React. I haven't encountered anything more appealing than this combo yet (Admittedly I haven't tried Vue yet)
Thus far I’ve really enjoyed https://xstate.js.org/. It’s fantastic for ensuring that your application is in an expected state and to control transitions between states. When it’s overkill you can often just drop to useState for simple stuff.
I've recently started working with React Query (now Tanstack Query since is supports Vue and others) and I absolutely love it. Coupled with a sprinkling of `useState` and context, I'm all set.
Of all of the SPAs I've worked on, 90%+ of the "state" is just server side data, pulled down via APIs, to display to the user. Other state libs don't bother to take into account common things like cache invalidation, or loading states, but it's all built in to React Query.
this, hands down is the best solution. I was using Apollo initially for my graphQL needs and started using that for local state too, but moved to urQL as found Apollo bit slow and bloated. I love the loading/err/res conventions carried over to react-query.
I think it depends if you are working on an app or on a library. In an app I would recommend to use Zustand or Jotai. If you prefer a Redux-like state management with a central store and actions to update it, go with Zustand, if you prefer a proxy-based atomic solution, go with Jotai. There might be situations in library code where it's useful to use React context but I would not use it in app code. It's too easy to use it wrong.
My native web components fetch their own state. They pass data to children via attributes and to parents via event bubbling. Very happy with this solution. Re-renders work using observableAttributes, which is part of the native component standard. It’s “just JavaScript” that everyone already understands and still avoids global state.
But while some of the UI I build is complex, I don’t build SPAs. If I did maybe this wouldn’t work as well.
These are the options I have used and tested heavily (only in React):
Redux:
- pros: framework to place different pieces of logic, makes it predictable once everyone is onboarded; boilerplate (actions, reducers, selectors, etc) makes it easier to avoid mistakes if you're not using TS
- cons: boilerplate adds a lot of friction that's not needed if you're using TS; debugging usually requires jumping through more files
XState:
- pros: what I said for Redux but on steroids
- cons: big setup (learning-wise); feels overkill 95% of the time
Context:
- pros: simple to understand and use (you expose values and setters via useContext); minimal setup (Provider and useContext)
- cons: you're still trapped inside React, your logic is inside a big component by default
Mobx:
- pros: keeps your logic out of React by default; you can export your model to another env and debug/test/play easily
- cons: most costly to setup (learning, config) out of all the options (redux with starter toolkit reduces the setup overhead)
I mainly use Context now as I port from Redux. Feeling the pain of not having an interactive model outside of my app views and want to get my team on Mobx.
If you think this is convoluted or overly complex (I see a lot of 'just use Javascript and DOM APIs') think about it for a second: how many libraries do you load in your backend projects? We have a lot of dependencies related to state management (database, ORM, caching solution, task queue, etc) - it's a big problem. Why wouldn't we use tooling to make our lives easier in JS/TS frontends?
Because the backend actually does something. Truth is 99% of frontends are just a way to display data just fetched from the server and don't really have a need for state management at all. For the 1% out there, use Redux (or Redux Toolkit), or maybe forget about React since it's not the only library ever invented.
For most CRUD apps it is, that's why we can have things such as Firebase which is essentially just a layer between the frontend and database, with some security rules and you're done.
Most developers overengineer to the extreme their projects. I'm not saying all of them are like this, or that all applications are just simple CRUDs, but most are.
You could very easily handle storing to localstorage or indexeddb using stores. However, in larger projects that can become error prone and increase complexity (unnecessarily).
To simplify this workflow you could use something like https://github.com/andsala/svelte-persistent-store
Note that repo is basically 50 lines of code to automate this stuff, which is pretty remarkable. However this still won't handle cache invalidation or syncing.
For handling the service workers stuff for (what I assume would be a PWA) I'd just use the Vite PWA plugin which uses workbox. That's if I need a service worker at all, if I don't I'd just set the manifest and be done with it.
An alternative depending on the needs of the specific project is to just fallback on something like the Firebase or Supabase SDK which handle offline mutations and syncing for you, for simpler projects these work incredibly well.
I can't recommend this sweet library (Valtio) enough, recently used this on a project and I'm never going back to redux/mobx/jatoi/zustland/context etc.
At my employer we have developed our own state management solution on top of react-easy-state. It's a good solution for us, it matches the backend more closely and has some conventions that work for our model. I wouldn't necessarily recommend it for other apps.
For personal stuff I like recoil and jotai. They're similar in design. Super simple to start, easy to type, work with layers of complexity.
Benefits of it over mobx is data normalization with references and JSON patches which allow you sync complex state easily. Typed models are also a plus.
And if I were to build a state management tool, I would prioritize a library that has
- immutability
- JSON patches and snapshots
- data normalization with references
- schemas / models compatible with zod
Recoil for single page apps. As simple as context but more readable. For another app the url was used as a single source of truth, so kind of a global state. The components just called useQueryParams.
For multi page apps (e.g. with nextjs) I’ve never used a global state manager. Next-auth, useReducer and react query are enough until now.
The url is the state, everything happens in response to url changes and everything changes the url.
Before I tried calling functionality and updating the url in response to user actions but since the state has to be exactly the same if you paste the url into the browser I got rid of the direct response.
Similar to some strong sentiments in this thread, we don't use common state managements. Mainly because they doesn't suit our need and requires us to learn more unnecessary patterns.
My company is building app with quite a set of complex features and structure (e.g. stateful integration with game consoles, background custom upload protocol, varying lifetime of modules, etc).
- The state management we always come back to is closure-based objects.
- Styled as agents, rather than passive storage (e.g. redux store-like construct). Thus providing separation of concern both in type definitions and code execution scheduling (can't find better words for these :'))
- In house event utility is used by the agents to communicate outwards.
- Combine with useEffect, useState, useMemo, and our in-house event utility to "escape" React's lifecycle by default and selectively subscribe to events, thus avoiding unnecessary-rerenders problems.
- Designed to abstract its domain and expose its internal data via methods.
- Optionally be designed to allow both code reuse and object reuse. Allowing devs to share or to not share states. Common use case for object reuse is agents acting as background-service propagated via React Context. It works really well with React Context.
- Optionally self-managing via buffers and async loop. The buffer is not exposed to its consumer directly, and the async loop works on the buffer.
- Can be useful for dependency injections too.
- Well isolated and optionally injectable so that you can unit-test against it without import mocks.
A caveat, this pattern isn't as usable when written with pure JavaScript. We relies heavily TS on FP patterns supported by fp-ts and io-ts as well as IDE's auto-complete and jsdoc to unload a whole stuff off our mind.
We've sometimes evaluated common state management solution and often found them requiring us to remember more things, but it doesn't yield valuable abstraction. In fact, most state management is abstracting the wrong thing.
When you refer to frontend state management, I'm assuming you're talking about libraries like Redux, and are using something like React. Basically, storing state that cuts across the component hierarchy.
First of all, I would suggest minimizing it as much as possible! You can often get away with a combination of component local state and data fetching libraries like SWR (the one I use) and react-query (I've heard it's good but haven't used). Modern data fetching libraries support intelligent caching, cache invalidation, and even optimistic updates.
The vast majority of internal tools at Twitter (prev employer where we built internal tools) didn't need any state management at all outside of swr and component local state.
However there will be some cases where some sort of client side state management will be useful. In these cases I reach for jotai. It was one of the first state management solutions designed post React Hooks, so it feels much more natural than state management solutions where hooks were bolted on. Additionally, it doesn't suffer from some of the rough edges that similar libraries like Recoil have (like naming fatigue from all those string keys).
Btw, I only use a tiny subset of jotai. I don't use any of the persistence or async stuff.
So tl;dr:
1. Build your app with component local state and SWR only.
2. If this gets messy, refactor your component local state and SWR code to be abstracted away as reusable functions / hooks.
3. Only when there is a performance problem or a really convoluted component hierarchy that doesn't make much logical sense, consider adopting jotai, but only for the parts of your app state that are causing the issues.
I remember I didn't have any concerns with state management when I was using emberjs. not sure why.
Now, working with react, I have successfully avoided redux and any extra state management like yhe plague.
Good abstractions, hook factories (under-disscussed topic imho), some sparse contexts. So far it has worked out great. I recently managed to implement quite complex requirements using these disciplines on a project while some other part of the same project contains redux-hell to do some simpler things
React specific but i’ve built a couple non-trivial applications with Recoil. I enjoy the ergonomics of it and building up a state graph. At its simplest it like a global version of useState which is really all you need, something you’re used to but happens to be shared. I prefer things that have simple primitives that can be composed into something complex. The documentation is good for describing what it does but not the best at easing you into it’s mental model.
One cool thing about Reagent (forgot where I found the quote): Reagent benchmarks showed it was faster than vanilla React due to how Clojure uses immutable data structures. The React team used it as inspiration for a big refactor (not sure if that's in it already, think the quote I found was from ~2017-18)
At work when I was on the front end side( our company uses Angular(honestly its dope, it should be more popular IMO) ) and we settled with ngrx(a redux clone), which was fine until I used react hooks & context for my own side projects. I work for a major airline so I guess ngrx is a nice to have for a multi page multi state SPA but seriously there is way to much boiler code with ngrx/redux.
Svelte stores. Mainly because the docs are practical and not a lecture about state management or functional programming and secondly because stores come with Svelte.
React seems to be drowning in its own complexity these days. As another poster points out you can do global state by observing an object on ‘window’, that most react state management tools make this more complex rather than simpler is concerning.
I try to stick with plain React hooks when I can. For small-medium state complexity they're usually enough
For big stuff, especially deeply nested trees, MobX is still my favorite. It has some quirks but the core premise is such a simple and ergonomic mental model to work with, even for very complex state with intermediate derivations
1. It's an add-on, so eg. you have to manually wrap each React component in an observer(), it doesn't really interact with hooks (triggering or responding to updates), etc
2. It has some minor gotchas around object cloning, proxies, memory usage, and stuff like eg. certain functions have to be pure
But in practice these aren't big barriers; once you get used to them they fade into the background. And I've made stuff with MobX that I'm not sure I could have built with any other state management system. It's also one of the best-performing ways to write React apps because it can be absolutely surgical about only re-rendering exactly the things that need to update, even moreso than the official state management system
Just use a global object and continuously run setState on the root component inside a requestAnimationFrame. Whenever you need to use or display something just reference or set the global object and you will be sure it's visually updated within the next rAF.
I'm not sure if this was a serious comment, but that sounds like a massive amount of performance overhead.
A way to optimize that approach substantially, while keeping everything else mostly the same, would be to have a setter on the global object, and only re-render the root component when that setter has updated the state.
My suggestion is not a very optimal approach, but probably a lot better than the continuous state loop.
My team is currently using this and I've been wondering if there are reasons to move to something like jotai or Zustand. The ergonomics of react-hooks-global-state with TS is quite nice already.
Keep it simple. Have global state where it needs to be global.
The easiest way to avoid problems with multithreaded writes to globals is to not have multithreaded writes to globals. Ensure all writes to said global only occur in one thread. One possibility is to use e.g. locks and semaphores to ensure only one thread writes at a time, but nothing can beat the amazing synchronity that is imperative programming. Try it out some time. Tell your friends.
The real secret here is that there are very few things that actually need to be globally writable. Most things that are global should be constant, so not a problem. For those which aren't, you probably have some kind of IPC message passing thing in your programming tool of choice. Use it to pass any state changes to global state to your main thread, which changes it. Be aware that race conditions may happen. If they cause you issues, make them not happen any more. Don't worry so much about life.
There will be problems with every approach. Pick one with few problems, and deal with them as they come. Multithreaded programming is inherently complex. No need to make it worse than it is.
A front end is simply an application that talks to a different application to receive its data instead of processing it directly. It can be as threaded as you like.
- Redux is cool but there’s so much decoupling that it gets hard to read the code. New hires would take too long to start, TS types got crazy, it was too much.
- XState is very good for state machines… but you barely ever need that level of control. Most of the time understanding it ends up being overhead. We’ve had maybe one good case for using it in about 10 years.
- React’s contexts are minimum overhead and work very well with “jump to definition” functionality and TS types. You can choose to use a reducer if you want, or skip it if it isn’t appropriate.
YMMV of course. This is just us.