perf overhead on context flattener?

Reilly,react

Someone shared this image above. At first glance, I wasn’t sure about the perf implications — so let’s break it down from first principles :D

Let’s deconstruct this monster

Here’s the code:

// Function to flatten React Context Providers. const flattenContextProviders = (providers) => { return providers.reduce( (acc, Provider) => { return ({ children }) => <Provider>{acc({ children })}</Provider>; }, ({ children }) => <>{children}</> ); }; // Array of Context Providers. const contextProviders = [ FirstContextProvider, SecondContextProvider, ThirdContextProvider, ]; // Flattened Context Provider. const FlattenedContextProvider = flattenContextProviders(contextProviders); // ⚠️ Really bad idea for performance! // It hides problem instead of solving it, and in really bad way. <FlattenedContextProvider> <App /> </FlattenedContextProvider>;

Off the top of my head I don’t know from intuition what flattenContextProviders becomes when called with the array so let’s construct it line by line:

// looks like this the first call const flattenContextProviders = ([ FirstContextProvider, SecondContextProvider, ThirdContextProvider, ]) => { return providers.reduce( (acc, Provider) => { return ({ children }) => <Provider>{acc({ children })}</Provider>; }, ({ children }) => <>{children}</> ); }; /* Here's every run for reduce first run acc = ({children}) => <>{children}</> Provider = FirstContextProvider returns ({children}) => ( <FirstContextProvider> {acc({ children })}} </FirstContextProvider> ) since our acc is ({children}) => <>{children}</> which is a function that takes a parameter called children and returns the children enclosed in jsx fragments and here in this line: {acc({ children })}} we're just calling the above function with {children} as the argument if that doesn't click we can work it out through substitution: ({children}) => ( <FirstContextProvider> { // define a function, then CALL it immediately with { children } (({ children }) => <>{children}</>)({ children }) // which becomes <>{children}</> } </FirstContextProvider> ) all in the first pass we're returning ({children}) => ( <FirstContextProvider> <>{children}</> </FirstContextProvider> ) */ /* second run acc = the return from the first pass: ({children}) => ( <FirstContextProvider> <>{children}</> </FirstContextProvider> ) Provider = SecondContextProvider returns ({children}) => ( <SecondContextProvider> {acc({ children })}} </SecondContextProvider> ) remember accumulator is now an anonymous function wrapping FirstContextProvider so the above becomes returns ({children}) => ( <SecondContextProvider> <FirstContextProvider> {children} </FirstContextProvider> </SecondContextProvider> ) I think at this point you see the pattern. Once you get past that weird IIFE it's all good :D so at the very end flattenedContext becomes this: ({children}) => ( <ThirdContextProvider> <SecondContextProvider> <FirstContextProvider> {children} </FirstContextProvider> </SecondContextProvider> </ThirdContextProvider> ) */

Perf implications

quick recap —
we have this:

const FlattenedContextProvider = ({ children }) => ( <ThirdContextProvider> <SecondContextProvider> <FirstContextProvider>{children}</FirstContextProvider> </SecondContextProvider> </ThirdContextProvider> );

which is an arrow function / function bound to a constant variable that returns a bunch of wrappers around children i.e. a react component.

Same rules of react apply here: don’t define components inside other components. So as long as you don’t do that there are no performance implications compared to directly wrapping your app around the same providers in the exact same location.

Other implications

What about in debugging? Does this wrapper hide the nested contexts under the name FlattenedContextProvider?

w/o wrapper

and with it

all it did was add an additional fiber “Anonymous” which we could rename via:
FlattenedContextProvider.displayName = "FlattenedContextProvider";

tldr;

This sort of structure — compared to manually nesting the same providers — is purely a matter of taste. It has no observability drawbacks and, most importantly, no performance overhead. :D

2026 © Reilly O'Donnell.RSS