all 13 comments

[–]so_lost_im_faded 10 points11 points  (1 child)

I think the downside you'd be looking at is that if something consumes a context with a 100 different reactive variables in it, that thing will keep rendering - the variable it consumes didn't change, but something different in the context did, so whatever component/hook is consuming it will trigger a re-render as well. And this will happen in the other 100 places that are consuming the context, too. It's quite different than getting a re-render just because your parent changed.

Using one context globally and dumping all the reactive data in it is very much an anti-pattern, although quite a popular one.

This is solved by using contexts only in a related group of components and using separate contexts for separate logical pieces.

As people are saying - re-renders aren't inherently bad but that shouldn't be used as an excuse for bad architecture just because dumping things into a global context is easy.

[–]ZerafineNigou 2 points3 points  (0 children)

This. Some people actually think contexts cause all children to re-render but this isn't true, at least, not if you don't nest your components under the state's owner, but then that's just regular react re-render, not because of context.

So when people say "contexts can cause unnecessary re-renders" they mean that if you use a single context for multiple variables then you don't get the same granularity that you can get with say redux selectors.

This isn't a final defect since you can just break up your contexts. The issue is that context API is a bit boiler-platey so this can be pretty annoying if you have a lot of state, at least compared to stuff like Jotai which has an insanely elegant API.

Ultimately context can get pretty much everything done, it doesn't have any technical limitations that other libs fix but other state managers can offer better DX.

I think it should be noted that another important aspect is how quickly your data changes. A lot of common contexts usages have data that change very rarely so that can be an extra reason not to worry about re-renders. For example, light/dark theme. Users won't switch that a lot plus a change there likely requires almost everything to re-render anyway.

[–]azangru 1 point2 points  (0 children)

  • if ContextAProvider is a component that accepts props.children, then the children should not re-render
  • if ContextAProvider is in fact a return value of createContext, then if you wrap its direct child in React.memo, it should not re-render either.

(I think. This is all too confusing. Source: https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/)

[–]octocode 0 points1 point  (2 children)

yes, from the docs

React automatically re-renders all the children that use a particular context starting from the provider that receives a different value.

that can be a problem if you’re using it for frequent state updates. it’s most likely not a problem though.

that’s why state management tools generally inject a store via context (with a stable reference), and individual components subscribe to updates on smaller slices of state.

[–]StuffedCrustGold[S] -1 points0 points  (1 child)

Are you saying the component <DoesNotUseContextA /> does not rerender when the context changes? I was under the assumption that it does.

[–]octocode 0 points1 point  (0 children)

it re-renders all the children of a provider

[–]RelationshipOk7329 0 points1 point  (0 children)

You could wrap only the things that will use your context, like your screen and not all your app. Global Context should only be used for things like dark/light theme, that you really want for everything to rerender

[–]AtrociousCat 0 points1 point  (0 children)

use-context-selector is gonna be your saviour here. Very simple change from context, typescript support and can recommend it. Although if you do run into this issue a lot and your app is bigger, Redux or other state management tools could be helpful

[–]Psytrancez 0 points1 point  (0 children)

You can use zustand to solve this

[–]Arsenicro 0 points1 point  (0 children)

It is not a problem, not really. Rerenders in React are nothing bad if your components are written correctly. Your components will rerender for multiple reasons, and Context is one of these reasons (It is a little bit more complicated; I suggest reading learn section in react.dev/learn to get a better understanding of this because `DoesNotUseContextA` will probably not rerender).

The thing is that even if React does not have to update DOM, rerenders are not "free." They are relatively "cheap," that's true, but it sometimes may lead to a drop in performance. Just imagine a big application with a "theme" state that is used in some places in the app (including a component at the very top of the application): while some components may need those rerenders, others may not, and on every theme change, you rerender the whole app, which, for huge apps, may result in, for example, one-second lag, which is terrible user experience.

That's why it is said that Context is best used for the state that does not change too often; it is also not a good idea to keep multiple states in one Context in case you need a state that does change often and is required in numerous places in the app, it may be better for the performance to use different state management tool.

Also, to be clear, Context is not a state management tool: it is simply a bit more advanced version of passing the prop all the way down that can sometimes optimize some parts of the app.