6

I have inherited a codebase where the previous owner has made extensive use of React.Context. This has resulted in what might be described as "context hell"

<AppContextProvider>
  <AnotherProvider>
    <AgainAnotherProvider configProp={false}>
      <TestProvider>
        <FooProvider>
          <BarProvider configHereAlso={someEnvronmentVar}>
            <BazProvider>
              <BatProvider>
                <App />
              </BatProvider>
            </BazProvider>
          </BarProvider>
        </FooProvider>
      </TestProvider>
    </AgainAnotherProvider>
  </AnotherProvider>
</AppContextProvider>;

This feels like an anti-pattern and is causing considerable cognitive overhead in understanding how the whole application works.

How do I fix this? Is it possible to use just one provider for everything? I previously used redux-toolkit with redux for managing state in react. Is there anything similar for contexts?

Patrick Roberts
  • 40,065
  • 5
  • 74
  • 116
spinners
  • 2,021
  • 1
  • 17
  • 31
  • 3
    You could use one provider for everything and make that really complicated, but I'm guessing that was what the original coder was trying to prevent using a number of bespoke contexts to deliver state to the app. I'd advise you to take some time to learn what's going on here before you make any drastic changes. – Andy May 10 '21 at 09:35
  • 2
    Is your objection purely to the syntactically deep indentation? Using contexts like this does not impede performance of the application, in fact quite the opposite. Since the `` component itself should be pure, this tree will never re-render, and only dispatches from within the application will ever re-render specific context providers (and all their consumers, as it should), skipping re-rendering of the context providers which are nested within. – Patrick Roberts May 10 '21 at 14:33
  • 1
    Perhaps a compromise: How to eliminate deeply-nested React context hell? Although... I mean, the canonical solution is pretty straight-forward: combine contexts. – Dave Newton May 10 '21 at 15:17
  • @DaveNewton disagree with your suggested canonical solution. Combining contexts will degrade performance. Specialized changes to provided values will re-render more consumers than necessary. Keeping contexts separate has a very clear benefit in that regard. Title suggestion is fine though. – Patrick Roberts May 10 '21 at 15:20
  • @PatrickRoberts I didn't say it was a *good* solution in all cases, I said "the way to eliminate deeply-nested contexts is to combine contexts". Whether or not it's "good" is context-dependent. – Dave Newton May 10 '21 at 15:21

1 Answers1

2

One way to get rid of additional contexts is by adding more variables to the value that is provided by a context.

For example if there are two contexts like UserContext and ProfileContext with providers like:

<UserContext.Provider value={user}... and

<ProfileContext.Provider value={profile}...

then you can merge them into a single context:

<UserProfileContext.Provider value={{user, profile}}...

Note: You shouldn't really merge all contexts into one single context because the reason they might be separated in the first place is that they represent different things but it makes sense to merge similar contexts.

Ramesh Reddy
  • 5,616
  • 2
  • 8
  • 23
  • 4
    Also, remember that contexts should also only be used if the value has to be used in many places and/or deep within the tree. Otherwise, just pass values as properties. – Peter Lehnhardt May 10 '21 at 09:34