diff --git a/apps/src/shared/gamma/containers/shared/use-components-by-name.ts b/apps/src/shared/gamma/containers/shared/use-components-by-name.ts new file mode 100644 index 0000000000..07cc040aeb --- /dev/null +++ b/apps/src/shared/gamma/containers/shared/use-components-by-name.ts @@ -0,0 +1,17 @@ +import React, { useMemo } from 'react'; +import type { StackRouteConfig } from '../stack'; +import type { TabRouteConfig } from '../tabs'; + +export const useComponentsByName = ( + routeConfigs: StackRouteConfig[] | TabRouteConfig[], +) => { + return useMemo(() => { + const map = new Map(); + + for (const config of routeConfigs) { + map.set(config.name, config.Component); + } + + return map; + }, [routeConfigs]); +}; diff --git a/apps/src/shared/gamma/containers/stack/StackContainer.tsx b/apps/src/shared/gamma/containers/stack/StackContainer.tsx index d5e0b25689..950827ef7d 100644 --- a/apps/src/shared/gamma/containers/stack/StackContainer.tsx +++ b/apps/src/shared/gamma/containers/stack/StackContainer.tsx @@ -21,10 +21,13 @@ import { useRenderDebugInfo, } from 'react-native-screens/private'; import { useParentNavigationEffect } from './hooks/useParentNavigationEffect'; +import { useComponentsByName } from '../shared/use-components-by-name'; export function StackContainer({ routeConfigs }: StackContainerProps) { useSanitizeRouteConfigs(routeConfigs); + const componentsByName = useComponentsByName(routeConfigs); + const [stackNavState, navActionDispatch]: [ StackNavigationState, React.Dispatch, @@ -62,7 +65,7 @@ export function StackContainer({ routeConfigs }: StackContainerProps) { return ( {stackNavState.stack.map( - ({ Component, options: { headerConfig, ...options }, activityMode, routeKey }) => { + ({ options: { headerConfig, ...options }, activityMode, routeKey, name }) => { const stackNavigationContext: StackNavigationContextPayload = { routeKey, routeOptions: { ...options }, @@ -73,6 +76,13 @@ export function StackContainer({ routeConfigs }: StackContainerProps) { setRouteOptions: navMethods.setRouteOptions, }; + const Component = componentsByName.get(name); + if (!Component) { + throw new Error( + `[Stack] No config matches the "${name}" route name`, + ); + } + return ( & { activityMode: StackScreenProps['activityMode']; routeKey: StackScreenProps['screenKey']; - isMarkedForDismissal: Boolean; // whether this route is during or after dismissal process + isMarkedForDismissal: boolean; // whether this route is during or after dismissal process }; /// StackContainer props diff --git a/apps/src/shared/gamma/containers/stack/reducer.tsx b/apps/src/shared/gamma/containers/stack/reducer.tsx index aac8c7ea3f..e7bc645a3d 100644 --- a/apps/src/shared/gamma/containers/stack/reducer.tsx +++ b/apps/src/shared/gamma/containers/stack/reducer.tsx @@ -270,8 +270,10 @@ function createRouteFromConfig( config: StackRouteConfig, activityMode: StackScreenActivityMode = 'detached', ): StackRoute { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { Component, ...rest } = config; return { - ...config, + ...rest, activityMode, routeKey: generateRouteKeyForRouteName(config.name), isMarkedForDismissal: false, diff --git a/apps/src/shared/gamma/containers/tabs/TabsContainer.tsx b/apps/src/shared/gamma/containers/tabs/TabsContainer.tsx index bb3910c7fe..958768392b 100644 --- a/apps/src/shared/gamma/containers/tabs/TabsContainer.tsx +++ b/apps/src/shared/gamma/containers/tabs/TabsContainer.tsx @@ -21,6 +21,7 @@ import { } from './reducer'; import { RNSLog } from 'react-native-screens/private'; import { TabsContainerItem } from './TabsContainerItem'; +import { useComponentsByName } from '../shared/use-components-by-name'; export function TabsContainer(props: TabsContainerProps) { RNSLog.info('TabsContainer render'); @@ -34,6 +35,8 @@ export function TabsContainer(props: TabsContainerProps) { useSanitizeRouteConfigs(routeConfigs); + const componentsByName = useComponentsByName(routeConfigs); + const [tabsNavState, dispatch]: [ TabsContainerState, React.Dispatch, @@ -84,7 +87,23 @@ export function TabsContainer(props: TabsContainerProps) { const pendingForUpdate = route.routeKey === tabsNavState.suggestedState.selectedRouteKey; - return + const Component = componentsByName.get(route.name); + if (!Component) { + throw new Error( + `[Tabs] No route config matches the "${route.name}" route name`, + ); + } + + return ( + + ); })} ); diff --git a/apps/src/shared/gamma/containers/tabs/TabsContainer.types.tsx b/apps/src/shared/gamma/containers/tabs/TabsContainer.types.tsx index e2f6f22b73..72f75c9205 100644 --- a/apps/src/shared/gamma/containers/tabs/TabsContainer.types.tsx +++ b/apps/src/shared/gamma/containers/tabs/TabsContainer.types.tsx @@ -27,7 +27,7 @@ export type TabRouteConfig = { /** * Runtime instance of a tab route. Created from a TabRouteConfig blueprint. */ -export type TabRoute = TabRouteConfig & { +export type TabRoute = Omit & { routeKey: string; }; diff --git a/apps/src/shared/gamma/containers/tabs/TabsContainerItem.tsx b/apps/src/shared/gamma/containers/tabs/TabsContainerItem.tsx index 68c8766665..bead2292da 100644 --- a/apps/src/shared/gamma/containers/tabs/TabsContainerItem.tsx +++ b/apps/src/shared/gamma/containers/tabs/TabsContainerItem.tsx @@ -37,7 +37,7 @@ function TabsContainerItemImpl(props: TabsContainerItemProps) { {...nativeOptions} screenKey={screenKey}> - {getContent(props.route.Component, safeAreaConfiguration)} + {getContent(props.Component, safeAreaConfiguration)} ); diff --git a/apps/src/shared/gamma/containers/tabs/TabsContainerItem.types.ts b/apps/src/shared/gamma/containers/tabs/TabsContainerItem.types.ts index 3bbf410bb2..8b5694eaae 100644 --- a/apps/src/shared/gamma/containers/tabs/TabsContainerItem.types.ts +++ b/apps/src/shared/gamma/containers/tabs/TabsContainerItem.types.ts @@ -1,9 +1,10 @@ import type { TabRoute, TabsNavigationMethods } from './TabsContainer.types'; +import React from 'react'; export type TabsContainerItemProps = { route: TabRoute; navMethods: TabsNavigationMethods; isSelected: boolean; pendingForUpdate: boolean; -} - + Component: React.ComponentType; +}; diff --git a/apps/src/shared/gamma/containers/tabs/reducer.tsx b/apps/src/shared/gamma/containers/tabs/reducer.tsx index ec14251ce8..81c93dfbae 100644 --- a/apps/src/shared/gamma/containers/tabs/reducer.tsx +++ b/apps/src/shared/gamma/containers/tabs/reducer.tsx @@ -135,8 +135,10 @@ function tabsActionSetOptionsHandler( } function createTabRouteFromConfig(config: TabRouteConfig): TabRoute { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { Component, ...rest } = config; return { - ...config, + ...rest, // Tab names are required to be unique (enforced by useSanitizeRouteConfigs), // so the name itself serves as a stable unique key. routeKey: config.name, @@ -205,4 +207,3 @@ function navStateWithConfirmedState( suggestedState: state.suggestedState, }; } -