Hacker News new | past | comments | ask | show | jobs | submit login
Ask HN: What is your favorite front end state management solution?
107 points by manx on Dec 25, 2022 | hide | past | favorite | 162 comments
Did you already build nontrivial applications with it? Where does it shine? What are the cons? What did you try before, which didn't work for you?



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.

YMMV of course. This is just us.


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.


This is not my experience at all.

I had senior developers, who have worked with RTK before, misusing simple features such as the "createSlice" API.

By contrast, I have never once sat down with a junior to walk through React's Context. I assume, they just read the docs and were good to go.


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:

- https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-...


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.


The usual misunderstandings about context are:

- 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.


Got any comment on the re-rendering argument? "Renders should be cheap anyway" or are you doing some specific optimizations?


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


What are your thoughts on MobX?


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.


Other poster here -

Success with MobX on a project. I worked as lead picking up an existing code base - none of us had any MobX experience so came in fresh.

My employer was kind enough to offer up FrontendMasters subscriptions - I watched a MobX course there over a couple of nights - link: https://frontendmasters.com/courses/redux-mobx/

I enjoyed it! Got the job done, no scaling issues


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.

Can you name examples of such state?


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.

I also heavily leverage https://github.com/neurosnap/robodux to treat redux as a database.

At the end of the day, redux is an event emitter (pub/sub) with a single object that stores all of your state that multiple components need to reuse.


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:

- https://redux.js.org/introduction/why-rtk-is-redux-today

- https://redux.js.org/tutorials/essentials/part-2-app-structu...

- https://blog.isquaredsoftware.com/2022/06/presentations-mode...


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.


Does it really? Hmm, I never noticed that. Will have to check for optimization opportunities then.

Does memoizing the state help?


> Does it really?

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.


It can be combinations of both. Many deeply nested small ones is convenient for objects/actors/agents/entities with different lifetimes.


No issues with needless rerenders?


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


This is your brain on Redux.

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().


Guess what, this entire thread is literally people discussing using context for… app state. But yeah thanks for being patronizing


Could you elaborate?


He's probably talking about this thread... https://news.ycombinator.com/item?id=34134290


FWIW I think hooks have made types a lot easier to deal with in Redux.


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:

- https://redux.js.org/tutorials/essentials/part-2-app-structu...

- https://redux.js.org/tutorials/typescript-quick-start#define...


I thought the issue with that is that it re-renders if any part of the context changes.


You’re correct and these people advocating for this are definitely re-rendering every component in their tree with every change


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.


Redux isn’t nonsense you’re just being dismissive and not taking the time to learn to understand it properly.


It just means you need more granular contexts right?


Right, but then you have a ton of contexts littered everywhere. Seems like that would be a lot of clutter.


Thanks for sharing

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


Good ol’ {}

Add anything you want to keep to it, store it in the window with window.app = {}

You can mix local storage in with it too if you want to write a class that checks its existence with a key.

Why must state management need a complex solution?

I’m sick of frameworks and modes where they abstract away the simplicity of javascript in favor of their way which is very economically driven.


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).


That assumes the rendering is done on the front end.

However, you could attach event handlers for state change monitoring & propagation.

I’m curious to know how scalable an object GP described would be for UI state (assuming it needs monitoring & propagating)


“Monitoring and propagating” — aka one of the global state management solutions being discussed here?

Redux state is “just an object” that does shallow comparisons to trigger re-renders


So now you’re re-implementing React.


Encapsulate it into two functions:

window.getState(); window.setState();

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.


Are you talking about using React, or some kind of home-rolled setState function on a simple object?


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)


Unfortunately, it's not so simple!

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.


There is definitely truth to this, I agree.


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).


Mithril isn't necessarily explicit, rerendering happens on various user interactions by default.


Funnily enough, that's where I'm at too. Just don't use a state manager at all.

There may be times when it's worth the cost, but otherwise just hang it on the global state and move on with your life.


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.


> Elm's state management paradigm was inspired by re-frame

Nope, it's the other way around


  Designed in late 2014, it slightly pre-dates the official Elm Architecture, although thankfully we picked up foldp ideas from early Elm games
https://day8.github.io/re-frame/re-frame/#why-should-you-car...


stated chronology doesn't imply inspiration


In React:

- Provided it is not an offline-first app, move as much state handling as possible out of the frontend

- use SWR or React Query for server state

- Keep pagination, filters, and other such options in your URL

- Hold state as local as possible. Only ever move state up if two or more branches of the tree need to be synchronised.

- Pass information down through props. That is what they are for. A little “prop drilling” is fine.

- Use CSS custom properties for theming

This gets you >95% of the way with very little complexity.

For the leftover pieces of global state like ‘isSidebarVisible’ I like Zustand because it is tiny both in bytes and concept.

I don’t like to use Context for state management because it litters the top-level with providers and causes unnecessary rerenders.

Avoid a single global store at all costs.


Zustand is great: https://github.com/pmndrs/zustand

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.


checkout his newer library Valtio from the same author


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.


For 98% of projects? A global state object that gets passed down the component tree.

This can scale to much greater complexity before it becomes an issue than many are willing to admit.


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.


Then you are prop drilling. Most projects I work on have hundreds of components and that would get old fast.


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?


emit and observe an event with a callback passed down?


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


oof


Can you expand on what you mean by "the paradigm"?



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.



Yes. This is the way. Once you use stores you realize how complicated state management was made.

Redux in particular was so dogmatic about it. So much boilerplate. State is ephemeral. There’s no reason to need one global store.


Is this like React contexts? I can't find a comparison of them (when I google it, it finds comparisons between svelte store and svelte context)


[dead]


No, Svelte stores don’t use effector under the hood, they’re most definitely hand written and aren’t using any dependencies at all.


I'm not too familiar with react but AFAIK contexts are a thing that you reach for only occasionally. Svelte stores are a fundamental concept.

Check out svelte, it's amazing.


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)
For React, I have to say that MobX was nice last I checked: https://mobx.js.org/README.html though React's own Context functionality is also okay: https://reactjs.org/docs/context.html


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 use the let keyword to store values that I know will change.


react-query to handle server updating state and per component hooks for local state items.

react-query has hook wrappers that allow accessing data you've fetched once across multiple components.

Calling the refetch in one will update all components using that data.

It's clean and minimises the need to think about shared state


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.


This. Along with a global state library of your choice for everything that is not server related data.


Mobx. Never looking back to any other React state management library


+1 for Mobx. I use it in Conjure[0] after watching a talk about how it’s used extensively in Linear[1]. I love it.

[0] https://conjure.so

[1] https://linear.app


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.


> Because the backend actually does something.

Yikes. We can be just as reductionist about the BE: it’s just a shim for accessing a database.


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.


How would you develop a web app that needs to work offline?


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.

https://sveltequery.vercel.app/ should handle most use cases. This is very similar to React Query (since it's from the same devs at https://tanstack.com/).

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 prefer sticking w/ the basics and using EventEmitter.

https://speaker.app has a UI I've been prototyping w/ this approach.


What is EventEmitter?


It’s Node.js’s event system. It has been backfilled ad-nauseam to the browser.



I like valtio, works w/ React or just js. Has subscribe, derive, and more.

https://github.com/pmndrs/valtio


In the react world, valtio is a breath of fresh air as it removes almost all boilerplate code.


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.


mobx-state-tree (https://mobx-state-tree.js.org/)

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.

Drawbacks are performance (see https://github.com/mobxjs/mobx-state-tree/issues/1267).

Previously was using immer, which I loved because of immutability but moved off since classes and OOP didn't feel as natural as in mst.

If I were to pick an alternative, might try redux with normalization https://redux.js.org/usage/structuring-reducers/normalizing-....

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


I use this whenever I'm free to, and I was surprised it never caught on more.


Redux Toolkit with Typescript is my choice for now.


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.


More like a cache for back end data but it covers a lot of the use cases for front end state management.

https://tanstack.com/query/v4


I use a simple 4 point system for state management:

https://github.com/prettydiff/wisdom/blob/master/state_manag...

I use it for an OS GUI here which fully loads in 200ms in Chrome (300ms Edge):

https://github.com/prettydiff/share-file-systems

When simple things, like state management, become complicated they are either over engineered or poorly planned.


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.


reagent. reacting to deeply nested maps of immutable data in a single location. this is the way.


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.


xState

Sometimes you really need a state machine and for me this is the go to library.

Pros:

-Visualizer. Non-tech people love the visualizer. Tech people as well really.

-Integrates easily with entities which are also implicitly state machines, like Promise.

Cons:

-Learning curve. I've already rewritten our state machine once because my original implementation was not idiomatic.

Mobx also works. Ngrx, Redux and the like just smear your logic all over the place, which is bad.


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


What are the quirks?


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 personal favourite is Zustand. But if you like proxy state then Valtio is really nice.


Jotai [1] is my go to, though I have never used it for anything too big.

[1] https://jotai.org/


On angular, we use ngrx (Reactive State for Angular) https://ngrx.io/


Svelte. Simple and obvious global stores.

When working with Flutter I like provider. With React I use Redux Toolkit, but saying I like it is a stretch.


Mobx


Svelte stores.


Trying Zustand for a mid-sized react project. So far pretty happy with it.


I'm the first to mention it: Remix run.

No need for complicated state on the client.


react-hooks-global-state which at this point is really just Zustand


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.


Agreed! Haven't had the need myself but zustand probably makes more sense for a richer selector interface, using reducer syntax, etc.


separate useState's for each piece of a global state, preserving it in a flat shape, no nested objects.


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.


I'm curious how you are doing multithreaded in the front-end. You're talking about modifying your global state from a web worker?


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.


we’re using ngrx.

Context: the app in a large enterprise Angular app (100+kLOC)


Global document object


Vue with VueRouter.


hear me out on this:

hypermedia as the engine of application state

https://htmx.org/essays/hateoas/


I like recoil.




Join us for AI Startup School this June 16-17 in San Francisco!

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

Search: