DEV Community

Cover image for Do you need a State Management Library?

Do you need a State Management Library?

Andrew Bone on September 25, 2021

The other day I was browsing LinkedIn when a poll caught my eye What's your favourite React State Management Library and why? Naturally, I fe...
Collapse
sgoulas profile image
sgoulas • Edited on

Context API is not a state management tool, neither it provides the benefits of one. It also forces rerenders across your application, degrading its performance. It should be used only for specific variables that you know beforehand they are not going to change often.

Collapse
link2twenty profile image
Andrew Bone Author

Which benefits are missing? Rerenders only happen for components that use the specific context that has a change. I generally have a different context for each dataset but try to have as few contexts as possible, this keeps rerenders to a minimum.

Collapse
sgoulas profile image
sgoulas • Edited on

A state management solution stores a value itself. Context does not store a value, it merely provides a point of access for said value. The parent component that provides the context to the children is the one responsible for "storing" the value. Context also does not provide any mechanism for handling side effects. If you want to handle a login authentication flow with classic state management tools you have thunks, observables, sagas etc. Context does not handle side effects. Context also does not offer time travelling debugging. At any given point you can not deterministitcally know how the value passed by context came to be, whereas with state management tools you can inspect the sequence of dispatched actions.

Also, on the topic of multiple contexts, I can't speak without checking an actual example (the one provided in the post uses a single context provider), but the moment the application becomes a little bigger, sustaining a large number of different contexts becomes an impossible task. You have to juggle between what provider wraps what part of the component tree and then be specific in how you consume the provided value in that part of the tree. The moment you want a component to access a context value that was not previously available to it you have to restructure your provider wrappers to accomodate for this change. And this brings me to my final point, application wide state management solutions mean that all the reducers listen to all the dispatched actions, they just don't match actions for which they don't have a corresponding case. This means that at any given point a component connected to the redux store can dispatch an existing action and trigger a change in the application wide state. You can't do that with context because you have to be constantly aware of the whole structure so that a component can be inside the specific wrapper tree it needs to be. And of course, just as I mentioned earlier, even then, the value is not stored in the store, it's stored in a parent component.

You can also read a post by acemarke, a redux maintainer and one of the creators behind redux toolkit explaining in much greater detail why context is not a substiture for redux, their differences and their respective use cases:

blog.isquaredsoftware.com/2021/01/...

Thread Thread
link2twenty profile image
Andrew Bone Author

Yes, I know context isn't a state management solution hence saying

When we use this API in tandem with a custom hook it gets a lot more powerful.

But I don't really know what more you need other than global state. There are different ways to interact with state, like dispatching, but any of these can be coded into your hook and a just syntax sugar.

As for them getting to complex and hard to manage, this isn't a problem I've faced so far but one I understand.

Thank you for linking the blog post I'll give it a read 😀

Collapse
ash_grover profile image
Ash G

Rerenders only happen for components that use the specific context that has a change.

Careful here. If you're maintaining a global context, any change in any of the nested data in its branches will trigger a re-render because the reference to root object has changed. And all state management libraries rely on reference checks to determine if something has changed.

In other words, any non pure-component or a component not wrapped inside React.memo() which uses Context API via useContext() etc., will re-render anytime there's a change in the global state. Redux and other state management libraries prevent this re-rendering from happening with nested states which can improve performance.

In a non-trivial app, using a state management library with memoized components can give you huge performance gains. Especially in a mobile app where you have a lot of data to show.

Collapse
zaimazhar97 profile image
zaimazhar97

Hi...
I am a beginner Vue guy, sorry I invaded to this post.

My question is kinda general tho...

For example, if one of our component holds the state of username and we accidentally forgot to include that component in certain pages that requires username state. Will the state of username actually gone when we visit the page with that missing component? I've never build a huge application with moving states from one component to others. I just store the state inside localStorage and access it whenever I want but sometimes it bothers me that user can manipulate the state by injecting their own JS script inside the console.

The question is, what is the best approach if I want to keep the state globally while still able to access/manipulate it easily?

Thanks.

Collapse
jamesthomson profile image
James Thomson

I just store the state inside localStorage and access it whenever I want but sometimes it bothers me that user can manipulate the state by injecting their own JS script inside the console

Just to be clear, this is the case regardless of if you're using localStorage or a state management lib. Anything on the client should be considered accessible to the user.

Collapse
zaimazhar97 profile image
zaimazhar97

I've been bugging myself for quite some time about this. Now I can stop concerning too much about keeping state in localStorage.

Thank you. 🙂

Thread Thread
jamesthomson profile image
James Thomson

Yes, don't worry about it unless it's sensitive information - that should never be stored on the client.

Collapse
link2twenty profile image
Andrew Bone Author

Hi, no need to apologise the more the merrier 😅

If we're talking about Vue specifically I'm afraid I don't really know it well enough to answer. I can link you to their documentation on state management though.

State Management — Vue.js

Collapse
zaimazhar97 profile image
zaimazhar97

Thank you ☺️

Collapse
krtirtho profile image
KR Tirtho

If you're using react-query/react-swr for data fetching/caching then Context API will be enough for managing the simple UI states

Collapse
link2twenty profile image
Andrew Bone Author

I tend to use vanilla fetch for my queries rather than a hook, though I do sometimes make a hook like my example one to handle the initial fetch and refreshes. Are there any advantages to using react query other than it makes initial setup time quicker?

Collapse
ivanjeremic profile image
Ivan Jeremic

Yes manny, mostly boilerplate you need to much boilerplate if you do everything yourself, I know it is not hard but it simply is more code.

Collapse
ivanjeremic profile image
Ivan Jeremic • Edited on

I use also react-query and also sometimes context for UI state but mostly recoil, what are you doing in context to avoid rerenders or do you simply don't care if components rerender?

Collapse
link2twenty profile image
Andrew Bone Author

What are you doing in context to avoid rerenders?

Good question, it is my understanding that a component will only rerender if the context in question is loaded in using useContext. Because of this I find it best to have several contexts for different types of data and only add the contexts where they are relevant.

For instance I may have a user data context that gets data from the server but then I may also have a permissions context that gets different data from the server but won't need to update if the user updates their profile picture.

By keeping contexts separate, and using them sparingly, you can prevent excessive redraws.

Thread Thread
ivanjeremic profile image
Ivan Jeremic • Edited on

Yes that is one way creating multiple contexts and making sure each context keeps only one piece of state never more than one, to avoid a bunch of code and boilerplate I recommend you to look at recoil it is just react under the hood no external deps. It does the same but without you need to take care how and where to inject the context

Collapse
krtirtho profile image
KR Tirtho

I use zustand in this case. It's the most efficient in this case

Thread Thread
ivanjeremic profile image
Ivan Jeremic • Edited on

Hard to use zustand when the creator of it himself says Jotai is better. They are both from the same team and Jotai is inspired by recoil.

Thread Thread
krtirtho profile image
KR Tirtho

What? When? I never saw him writing such thing. Not even on the README. BTW, zustand has double stars in Github compared to jotai

Also, zustand has over 113k downloads per week in npmjs.com where jotai is only has 5-7k downloads per week

Thread Thread
ivanjeremic profile image
Ivan Jeremic

It was a tweet.

Collapse
victorocna profile image
Victor Ocnarescu

I use Redux in my React apps for one and only one purpose: to store in app memory the JWT when handling authentication (because saving it in local storage would be madness). And I chose Redux because it was very popular a few years ago.

I also use React Context to store app wide config info like theming, language. However I do not use any other "global" state, react-query is more than enough for displaying API results in the frontend.

Cheers!

Collapse
mapleleaf profile image
MapleLeaf

It sounds like you don't need redux 🤔

Collapse
victorocna profile image
Victor Ocnarescu

That may be true, but what else? I need something non persistent to store some app state and I believe Redux is just that: a state container

Thread Thread
mapleleaf profile image
MapleLeaf

You can put it with the rest of your app state, or send it through another context provider.

Thread Thread
victorocna profile image
Victor Ocnarescu

Of course. But I wanted decoupled from my app state because I can reuse this principle for other apps that do not use React. It never actually happened until now but that's what I thought when I decided to use Redux: to keep things completely separated.

Collapse
rowlinsonmike profile image
Michael Rowlinson

Zustand is simple to learn and powerful. Easy manipulation inside and out of components. Very little boilerplate. Many performance optimizations baked in.

Collapse
link2twenty profile image
Andrew Bone Author

I've never heard of Zustand, what would you say its main benefits are?

Collapse
rowlinsonmike profile image
Michael Rowlinson

From my experience, I like that it is dead simple. Recoil is close but still requires Context providers. I like that state is easily manipulated outside of components as well. There are many complex use cases in the docs implemented in very clever intuitive ways, like its use of immer for reductions and transient updates.

Thread Thread
jerevick83 profile image
jerevick83

How do you work around it's persistency? I tried using it to persist basic user data but that became messier as there was lots of persisted data in the localstorage for even signed out users. I mean lots and lots of them.

How do I persist and clear the entire storage after signing out?

Collapse
code913 profile image
code913

I have a question.
Why would someone need a state management library?
Isn't useState enough?

(I'm an amateur react dev, pls don't sue me)

Collapse
link2twenty profile image
Andrew Bone Author

Great question 😊

useState is great but is component specific. You can share the state with props and even share functions to modify the state in the same way but as your app gets bigger this can become complex.

Say you have an shopping app that lets you add items to your cart before you buy them. Each product pages needs to know if they're in the cart and if they are how many are, the checkout page needs to know the cart contents and the app bar needs to know how many items are in the cart to display a number.

You could store the cart state at top level and pass functions and states to relevant children via props or you could handle it using a context. This makes the code easier to understand and, generally, run faster.

Does that make sense?

Collapse
code913 profile image
code913

here's a cursed piece of code to achieve the same thing

window.shoppingCart = []
window.updateCart = s => shoppingCart.join("") !== s.join?.("") && shoppingCart = s;
Enter fullscreen mode Exit fullscreen mode
Thread Thread
link2twenty profile image
Andrew Bone Author

Yeah that is heading the right way but when this changes it won't trigger a redraw though, which is the whole point of react. You can use local/session storage though.

Thread Thread
code913 profile image
code913

I could spend the rest of my day somehow making that work. I have a special ability of writing cursed code :^)

Thread Thread
link2twenty profile image
Andrew Bone Author
Collapse
latobibor profile image
András Tóth

My favorite is overmindjs which I would describe as if somebody who codes regularly rewrote redux to be actually "ergonomic" without the false functional programming paradigm and with an actual smart TypeScript implementation that works. It still has issues with handling deep nested values, but it is many steps to the right direction.

So to answer your question: in very simple cases you don't need a library. If your state grows, you have separate menus with their separate stuff, you will need namespacing, grouping. If you have complex set of state transformations (like having editing mode and then go back to preview mode) you will need a state machine as well (overmindjs also has it).

I think why people want to get rid of state management libs are coming from design mistakes that made them not ergonomic.

Collapse
rakeshsinghjamwal profile image
rakeshsinghjamwal • Edited on

Honestly, I haven't still learned any state management library yet. I am still working with context api, useReducer and yet don't feel the need to use one. But may be in one of the upcoming projects I will be using one. More than a state management library I am more keen to learn data fetching library like react-query.

Collapse
evanburg profile image
Evan Burgess

I largely agree with this. Only on very large projects where complex state management is necessary do I ever reach to implement Redux or alternatives.
React Context provides a great interface that many will feel comfortable with out of the box and will be enough in about 80% of scenarios really.

Collapse
moialbla profile image
Moisés Álvaro Blanco

In my humble opinion I use state management only if a way back of the data is needed, e.g. the user is filling in the data and the next step modify the provided data however the user could go back to review the previous data again, cart applications and similar or microfrontend (well here you will find a lot of discussions for and against using data sharing via microservices).
Summing up every application where the user could close the browser and log in again and we want to have ready all the data that was provided.
If we don't have these situations, we are adding extra effort for nothing when the browser and JS tools would be enough (LocalStorage, SessionStorage, Cookies, Cache, etc)
There is a tendency to add state management in every application because it is cool and the applications are overkill.

I hope it helps.

Thanks.

Collapse
okrohan profile image
Rohan Salunke • Edited on

I see some comment against Context API. It's definitely a feasible state management solution. Most of the current state management libraries already use Context API internally. In the end it's all about your use case, no one state management methodology is perfect or fits all purpose. It's all contextual based on factors like.

  • UI and Data coupling
  • Integration with UI
  • Server paradigm

This article might help you choose the right solution based on your use case. dev.to/swyx/why-do-webdevs-keep-tr...

Collapse
r12esh profile image
Ritesh Gupta

Yes, when working for a Medium to big project redux makes life easier. And then redux-saga makes Api calls so much organised. And the biggest difference is Context API is expensive.Context API prompts a re-render on each update of the state and re-renders all components regardless. Redux however, only re-renders the updated components

Collapse
link2twenty profile image
Andrew Bone Author

Slight caveat Context API updates all components that use the specific context updated. Provided you split the data across several contexts and only import the context in components that need access the redraws become a non-issue.

Collapse
raibtoffoletto profile image
Raí B. Toffoletto

O use a mix of local state, hooks/context and redux at work. We apply them where it makes sense to get the most of them. A complex hook may be better implement as a redux slice, in the same way a slice that doesn't implement async logic and has a fairly simple state is better off as a hook. The key is find the balance where a global state is needed and where you should keep the state local to the component. All depending on the size of tge application. 😉

Collapse
asologor profile image
Andrew Sologor

Firstly, you are not the first guy with such idea. Secondly, I agree with sgoulas. You NEED a state management library for the global state. Just because context wasn't created for global states. If you somehow avoid redundant re-renders of everything, it will eventually become unmanageable.

Collapse
link2twenty profile image
Andrew Bone Author

100% I am not the first person to think of this but I'm just sharing my take on it because a question was asked.

I'm yet to have a project where using context to handle global state has become overly complex, though you do have to think about the context and plan out the app first.

Thank you for your input

Collapse
bennodev19 profile image
BennoDev

I prefer to use AgileTs,
as it allows the easy creation of individual and independent States, which (in my opinion) are more straightforward and performant to handle. These States are even accessible outside the React Tree, which allows me to decouple my business logic from the UI.

Also, it has some nice add-ons like a Form-Manager, Event-Handler, ..

Example

Collapse
leonblade profile image
James Stine

I've been using Recoil for a fairly large project, overall it's been really good. One issue is with tests it becomes a little bit of a struggle working with it because you can't read Recoil values in tests. Otherwise, it's been nice to have and a lot easier than other options we looked at.

Collapse
thevediwho profile image
Vaibhav Dwivedi

Here's an interesting fact I wanted to share after reading this. When I started learning React in 2017, I built a big project for a client in React.

What's crazy is that I didn't use state management at all in it. But now that I look back at it, I feel like it could've been made much better with it. Anyhow, great read.

Collapse
link2twenty profile image
Andrew Bone Author

That is interesting. I try to use a few states as possible myself though, of course, still use a few.

For instance it's common to see a snippet like

  const [input, setInput] = useState();

  const displayValue = () => {
    console.log(input);
  }

  return (
    <>
      <input value={input} onChange={({target}) => setInput(target.value)} />
      <button onClick={displayValue}>Display</button>
    </>
  )
Enter fullscreen mode Exit fullscreen mode

Which takes the state away from the input and stores it in React. The only problem with this is it now takes process time, even though it's only a small amount, between each key stroke.

I'm much more likely to do something like

  const input = useRef(null);

  const displayValue = () => {
    const { current: el } = input;
    console.log(el.value);
  }

  return (
    <>
      <input ref={input} />
      <button onClick={displayValue}>Display</button>
    </>
  )
Enter fullscreen mode Exit fullscreen mode

This has the exact same outcome but will only need process time when the button is pressed rather than on every key stroke.

Collapse
andykras profile image
Andrey Krasnov

I'd prefer to use composition and render-props in this example.

Also I'm wondering why did you name your components function App, is this a typo?

Anyway agree with you that sometimes context API is a fairly good replace of complex state management.

Collapse
link2twenty profile image
Andrew Bone Author

Yeah the example I made was very simplistic and not one you'd use out in the wild but the principle is the same.

The naming of the components was just me writing a quick demo, the code is not production ready, that being said I've updated the function names now.

Collapse
hoandt profile image
hoandt

useContext and probably UseQuery from react-query are what I need.

Collapse
link2twenty profile image
Andrew Bone Author

What is it you like about useQuery? I tend to write my own fetch requests, even though that can lead to code repetition.

Collapse
hoandt profile image
hoandt

UseQuery caches fetched data. I would create a custom hook to handle the cache. Like useContext, you can get that cache globally.

It’s very handy to me when I apply it to store logged in user details.

Collapse
elliotandres profile image
Elliot Andres

I think we dont need State Management Libraries. React itself has everything necessary.

Collapse
swagwik profile image
Sattwik Sahu

Zustand?

Collapse
bolonhadev profile image
Bolonha Dev

Context + intelligence + reactive programing works for me.

Collapse
molimat profile image
Molimat

I totally agree that context api should be enough, but it has a lot of boiler plate and some times it's difficult to make it organize. That's why I like the way recoil and mobx manages it.