ModuleProvider
Opens a scoped module around its children: it constructs the module, initializes it, and owns its mount, unmount and destroy. What a scope is and where one belongs is on Modules; this page is the API surface.
Signature
ModuleProvider(props: ModuleProviderProps): JSX.Element
type ModuleProviderProps = ModuleParams & {
deps?: unknown[]
children?: ReactNode
}
type ModuleParams = {
id?: string
providers?: readonly ProviderInput[]
onModuleInit?: ModuleHook
onModuleMount?: ModuleHook
onModuleUnmount?: ModuleHook
onModuleDestroy?: ModuleHook
}import { ModuleProvider } from "@remodulo/react"
function OrdersPage({ customerId }: { customerId: string }) {
return (
<ModuleProvider
providers={[OrdersApi, OrdersStore]}
deps={[customerId]}
onModuleMount={(resolver) => resolver.resolve(OrdersStore).load(customerId)}
>
<OrdersView />
</ModuleProvider>
)
}OrdersApi and OrdersStore live and die with this boundary. A new customerId changes deps, so the module is destroyed and rebuilt with fresh instances, and onModuleMount loads again.
id
Optional; defaults to a generated one. It is what ModuleTraversal's findAncestorById and findDescendantById look up, and what error messages name the module by.
providers
A ProviderInput is a provider or a feature. The array is read when the module is constructed and never again - a different array on a later render changes nothing until the module is rebuilt. There is no registration after construction.
The four module hooks
| Hook | Fires | Times |
|---|---|---|
onModuleInit | during init, before every provider's | once |
onModuleMount | when the boundary mounts | as many as React says |
onModuleUnmount | when the boundary unmounts, after every provider's | as many as React says |
onModuleDestroy | when the module dies | once |
Each hook is called with the module's own Resolver. ModuleHook is (resolver: Resolver) => unknown, so a hook reads through the module it belongs to without a component around it.
An inline arrow is fine. The provider always calls the latest render's function, and passing a new one does not rebuild the module. Only deps, a new parent, or an explicit rebuild do that.
deps
Follows React's hook-deps rule exactly: element-wise Object.is plus a length comparison, and undefined on either side never triggers. Anything that changes destroys the module and builds a new one, with new instances throughout. The comparison runs on a layout effect, so several render attempts with the same new dependency build one module.
A parent module is required
The module forks the nearest module above it. With none in context - no <AppProvider> at the root, no enclosing boundary - construction throws:
ModuleProvider requires a parent module in context. Wrap it in <AppProvider>, or nest it under another <ModuleProvider>.A new parent means a new module. When the module in context changes identity, this provider builds a fresh module under it and the previous one is torn down.