Avoiding deep nested tree of React Context Providers
Alexey Korepanov
by Alexey Korepanov
1 min read

Categories

This article gives you a way to convert your tree of React Context Provider components into a flat structure.

Many React application with multiple contexts end up with a deep nested tree of context providers, something like this:

const AppWithProviders = () => (
  <ThemeContext.Provider>
    <UserContext.Provider>
      <SomeOtherContext.Provider>
        <YetAnotherContext.Provider>
          <OneMoreContext.Provider>
            <AndOneMoreContext.Provider>
              <AndSoOnContext.Provider>
                <AndSoForthContext.Provider>
                  <App/>
                </AndSoForthContext.Provider>
              </AndSoOnContext.Provider>
            </AndOneMoreContext.Provider>
          </OneMoreContext.Provider>
        </YetAnotherContext.Provider>
      </SomeOtherContext.Provider>
    </UserContext.Provider>
  </ThemeContext.Provider>
)

In my company we have more than 20 such levels and the app is far from completion!

To make the structure more flat you can use this function:

const BuildProviderTree = (providers) => {
  if (providers.length === 1) {
    return providers[0];
  }
  const A = providers.shift();
  const B = providers.shift();
  return BuildProviderTree([
    ({ children }) => (
      <A>
        <B>
          {children}
        </B>
      </A>
    ),
    ...providers,
  ]);
};

Then redefine AppWithProviders function like this:

const Providers = BuildProviderTree([
  ThemeContext.Provider,
  UserContext.Provider,
  SomeOtherContext.Provider,
  YetAnotherContext.Provider,
  OneMoreContext.Provider,
  AndOneMoreContext.Provider,
  AndSoOnContext.Provider,
  AndSoForthContext.Provider,
]);

const AppWithProviders = () => (
  <Providers>
    <App/>
  </Providers>
);

Nice and flat!

Update. I have released an NPM package for that: react-array-to-tree. It also supports component attributes:

const Providers = BuildProviderTree([
  [ThemeContext.Provider, {param1: value1, param2: value2},
  [UserContext.Provider],
  [SomeOtherContext.Provider],
]);

will be converted to

<ThemeContext.Provider param1=value1 param2=value2>
  <UserContext.Provider>
    <SomeOtherContext.Provider>
  ...
    </SomeOtherContext.Provider>
  </UserContext.Provider>
</ThemeContext.Provider>