DEV Community

Cover image for 🔺 Flat React Doom Pyramid in 1LOC
Andrew Luca
Andrew Luca

Posted on • Updated on

🔺 Flat React Doom Pyramid in 1LOC

Everyone was happy when React Team announced the new stable Context API, and everyone ditched Redux. But that's not the point.

After 1 year every codebase entrypoint looked like this at least.

<I18nProvider>
  <DataProvider>
    <ActiveDialogProvider>
      <PublicFetchProvider>
        <AuthProvider>
          <PrivateFetchProvider>
            <AuthFetchProvider>
              <CustomThemeProvider>
                <CustomMuiPickersUtilsProvider>
                  <LegalsProvider>
                    <PaymentMethodsProvider>
                      <CartProvider>
                        <App />
                      </CartProvider>
                    </PaymentMethodsProvider>
                  </LegalsProvider>
                </CustomMuiPickersUtilsProvider>
              </CustomThemeProvider>
            </AuthFetchProvider>
          </PrivateFetchProvider>
        </AuthProvider>
      </PublicFetchProvider>
    </ActiveDialogProvider>
  </DataProvider>
</I18nProvider>
Enter fullscreen mode Exit fullscreen mode

Soooo, should we do something about this? Most of the times there is no reason. Nevertheless, here is a simple performant solution in 1 line of code

const Pipe = (p) => p.children.reduceRight((c, e) => ({ ...e, props: { ...e.props, children: c }}));
Enter fullscreen mode Exit fullscreen mode

You can name it however you want:

  • Flatten
  • Compose
  • Pipe
  • Squash
  • Doom 😂
  • Nest
  • Inflate

And how will look above example? Better!

<Pipe>
  <I18nProvider />
  <DataProvider />
  <ActiveDialogProvider />
  <PublicFetchProvider />
  <AuthProvider />
  <PrivateFetchProvider />
  <AuthFetchProvider />
  <CustomThemeProvider />
  <CustomMuiPickersUtilsProvider />
  <LegalsProvider />
  <PaymentMethodsProvider />
  <CartProvider />
  <App />
</Pipe>
Enter fullscreen mode Exit fullscreen mode

This function component takes all its children and nests them from first to last, where first one will be the most outside the tree, and the last one will be last in the tree.

Here is one more variation with TypeScript and different API

function Flatten(props: PropsWithChildren<{ elements: ReactElement[] }>) {
  const { elements: e, children: init } = props;
  return <>{e.reduceRight((c, e) => cloneElement(e, { children: c }), init)}</>;
}
Enter fullscreen mode Exit fullscreen mode

And the usage will be like this:

<Flatten
  elements={[
    <I18nProvider />,
    <DataProvider />,
    <ActiveDialogProvider />,
    <PublicFetchProvider />,
    <AuthProvider />,
    <PrivateFetchProvider />,
    <AuthFetchProvider />,
    <CustomThemeProvider />,
    <CustomMuiPickersUtilsProvider />,
    <LegalsProvider />,
    <PaymentMethodsProvider />,
    <CartProvider />,
  ]}>
  <App />
</Flatten>
Enter fullscreen mode Exit fullscreen mode

I went through multiple iterations, and in the end I prefer first Pipe one liner that I showed.

Inspired by React Flat Multi Providers

Sandboxes experiments:

Thanks for coming to my TED talk! :)

Cover Photo by Kévin et Laurianne Langlais on Unsplash

Discussion (9)

Collapse
sincovschi profile image
Eugene

The Pipe it's looking really neat!
Although, I'd preffer more explicit usage with clearly separated concerns like TS version.

Collapse
ygormartins profile image
Ygor Martins

Agreed! The first approach can be pretty confusing and overwhelming to look at

Collapse
iamandrewluca profile image
Andrew Luca Author

Now I also agree. Looking at first version looks confusing.

Collapse
donkarolel profile image
Karolis Juškauskas

Great idea, however, what if you moved all of the providers to the Pipe component?

Collapse
iamandrewluca profile image
Andrew Luca Author

Can you please elaborate? 🤔 That was the point. You write the code flat, and the Pipe will make them in a pyramid shape automatically

Collapse
donkarolel profile image
Karolis Juškauskas

You could have all of the providers inside of the, say, Pipe component and then you could do just something like this: <Pipe><App/></Pipe>.

Thread Thread
iamandrewluca profile image
Andrew Luca Author

Yeap

Thread Thread
donkarolel profile image
Karolis Juškauskas

Neat.

Collapse
gillarohith profile image
Rohith Gilla

Haha good one