I am attempting to tidy my App.js
file by using a function to inject a nested array of providers.
Previously I had something that looked like:
<CreditsProvider>
<BundlesProvider>
<ContactsProvider>
<PhoneVerificationProvider>
<PurchaseProvider>
<RanksProvider>
<RoleWizardProvider>
<BasesProvider>
<DepsProvider>
<TitlesProvider>
<UrlsProvider>
<Main />
</UrlsProvider>
</TitlesProvider>
</DepsProvider>
</BasesProvider>
</RoleWizardProvider>
</RanksProvider>
</PurchaseProvider>
</PhoneVerificationProvider>
</ContactsProvider>
</BundlesProvider>
</CreditsProvider>
I built a function to do this automatically:
export function 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,
]);
}
Back in App.js
I fee the function the list of providers and add it to my app.
const Providers = BuildProviderTree(
...[
HeaderBarProvider,
CreditsProvider,
BundlesProvider,
ContactsProvider,
PhoneVerificationProvider,
PurchaseProvider,
RanksProvider,
RoleWizardProvider,
BasesProvider,
DepsProvider,
TitlesProvider,
UrlsProvider,
]
);
<NotificationBarProvider>
<NotificationBar />
<Providers>
<Main />
</Providers>
</NotificationBarProvider>
Everything works fine, however when I use my React dev tools to inspect the component structure I notice a bunch of nested Anonymous elements.
I have tried changing my export declaration as mentioned in a comment and this didn't work.
As I dug in a bit further I noticed all of the Anonymous elements are coming from the <A>
elements in the function while the named providers are the <B>
elements.
The anonymous functions are your ({ children }) => ( ... )
functions.
HeaderBarProvider
, CreditsProvider
), the new array looks like this:[
({ children }) => ( // <-- Anonymous function, new 1st element of array, will be `A` the next time
<HeaderBarProvider> // `A`
<CreditsProvider> // `B`
{ children }
</CreditsProvider>
</HeaderBarProvider>
),
BundlesProvider, // <-- second element of array, will be `B` the next time
ContactsProvider,
PhoneVerificationProvider,
// ...
]
BundlesProvider
), the new array looks like this:[
({ children }) => ( // <-- Anonymous (new first element of the array)
({ children }) => ( // <-- Anonymous (was previously the first element of the array)
<HeaderBarProvider>
<CreditsProvider>
<BundlesProvider>
{ children }
</BundlesProvider>
</CreditsProvider>
</HeaderBarProvider>
)
),
ContactsProvider, // <-- new second element of array
PhoneVerificationProvider,
// ...
]
You should be aware of what happens at "React build time" (when components are created),
and what happens at "React run time" (when React creates the elements from the components).
(the terms "build time" and "run time" might not be precisely correct here)
<A>
and <B>
are created at "run time"With your approach - of creating one HOC (Higher Order Component) to combine 2 components at a time, at "build time" - you can not avoid creating these wrapper functions. You can not change how the function (i.e. component) looks like, so the only way is to create a new component that wraps the original component with modified props. That new component is that anonymous function.
What you can do is you can define at "run time" how components should be rendered into elements. I.e. you could compose the Elements at runtime, instead of the Components at build time. So you only need one HOC, which creates the elements:
export function BuildProviderTreeAtRuntime(originalProviders) {
const compose = function(providers, children){
if( providers.length < 1 ){
return children;
} else {
const CurrentComponent = providers.pop();
return compose(
providers,
<CurrentComponent key={'id_' + providers.length + '_' + CurrentComponent.name}>{ children }</CurrentComponent>
);
}
}
return function ComposedElements({ children }){ // <-- only one wrapper function (HOC), which creates all the nested elements
const providers = [ ...originalProviders ];
return compose(providers, children);
};
}