ModuleScope
Turn a Host instance into a reusable scope (Provider + use + useImported).
ModuleScope packages “create a Host module instance + a Context Provider + useHost()” into a reusable scope.
It mainly solves two problems:
- Avoid prop drilling: deep modals/subcomponents can directly access the Host handle that belongs to the current route/page scope.
- Fix lifecycle ownership: the module instance lifetime is owned by the route/page boundary Provider, not by a modal component’s mount/unmount.
Usage
import { ModuleScope } from '@logixjs/react'
import { RouteHost } from './modules'
export const RouteHostScope = ModuleScope.make(RouteHost.impl, { gcTime: 0 })Attach the Provider at the route/page boundary:
export function RoutePage({ routeKey }: { routeKey: string }) {
return (
<RouteHostScope.Provider options={{ scopeId: routeKey }}>
{/* page body */}
{/* modals */}
</RouteHostScope.Provider>
)
}Get the Host handle in a child component (when you need the host itself or multiple submodules):
const host = RouteHostScope.use()More commonly: directly get an imported submodule handle (sugar):
const modalA = RouteHostScope.useImported(ModalA.tag)Bridge (advanced)
If your component is not in the route/page React tree (e.g. a global overlay rendered with a separate createRoot), but you still want to reuse the same “route scope (Host instance)”, use Bridge:
export function OverlayRoot({ routeKey }: { routeKey: string }) {
return (
<RouteHostScope.Bridge scopeId={routeKey}>
{/* You can keep using use()/useImported() here */}
</RouteHostScope.Bridge>
)
}Prerequisites:
- The route/page boundary has a corresponding
<RouteHostScope.Provider options={{ scopeId }}>(Bridge only “retrieves and reuses”; it does not create). - The root hosting the Bridge and the root hosting the Provider must share the same app runtime (the same runtime tree).
- The runtime must include the infrastructure required by Bridge: this is satisfied by default when you create the runtime with
Logix.Runtime.make(...). If you assemble the runtime manually, refer to the advanced docs (not expanded here).
Failure semantics:
- If
scopeIdis not registered or has been released (owner Provider unmounted / changed scopeId),Bridgethrows a readable error (to avoid silently reusing or returning a wrong instance).
Notes
Provider.optionsis forwarded to the internaluseModule(HostImpl, options)(whereoptions.scopeIdis mapped to internaloptions.key).scopeIddistinguishes/reuses a scope (same scopeId reuses; new scopeId creates a new instance).use()/useImported()throws immediately if the Provider is missing (it does not silently fall back to a “global singleton”).