From e0bb90f2fd534c46a664afd6d8bc2910d2a27b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20Gaczo=C5=82?= Date: Tue, 21 Apr 2026 16:39:39 +0200 Subject: [PATCH 1/6] TabsContainer refactor now, instead of keeping a Component in state, we find one in routeConfigs prop, basing on routeKey --- .../gamma/containers/tabs/TabsContainer.tsx | 22 ++++++++++++++++++- .../containers/tabs/TabsContainer.types.tsx | 2 +- .../containers/tabs/TabsContainerItem.tsx | 2 +- .../tabs/TabsContainerItem.types.ts | 1 + .../shared/gamma/containers/tabs/reducer.tsx | 5 +++-- 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/apps/src/shared/gamma/containers/tabs/TabsContainer.tsx b/apps/src/shared/gamma/containers/tabs/TabsContainer.tsx index bb3910c7fe..c9050a85d4 100644 --- a/apps/src/shared/gamma/containers/tabs/TabsContainer.tsx +++ b/apps/src/shared/gamma/containers/tabs/TabsContainer.tsx @@ -84,7 +84,27 @@ export function TabsContainer(props: TabsContainerProps) { const pendingForUpdate = route.routeKey === tabsNavState.suggestedState.selectedRouteKey; - return + const matchingConfig = routeConfigs.find( + config => config.name === route.routeKey, + ); + if (!matchingConfig) { + throw new Error( + `[Tabs] None config matches the "${route.routeKey}" routeKey`, + ); + } + + const Component = matchingConfig.Component; + + 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..0c0db001d1 100644 --- a/apps/src/shared/gamma/containers/tabs/TabsContainerItem.types.ts +++ b/apps/src/shared/gamma/containers/tabs/TabsContainerItem.types.ts @@ -5,5 +5,6 @@ export type TabsContainerItemProps = { 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, }; } - From 1d462c0ce84632a9bc749542e0222136e6391b3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20Gaczo=C5=82?= Date: Wed, 22 Apr 2026 12:59:53 +0200 Subject: [PATCH 2/6] StackContainer refactor analagous to TabsContainer also small change in TabsContainer --- .../gamma/containers/stack/StackContainer.tsx | 13 ++++++++++++- .../gamma/containers/stack/StackContainer.types.tsx | 2 +- apps/src/shared/gamma/containers/stack/reducer.tsx | 4 +++- .../shared/gamma/containers/tabs/TabsContainer.tsx | 4 ++-- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/apps/src/shared/gamma/containers/stack/StackContainer.tsx b/apps/src/shared/gamma/containers/stack/StackContainer.tsx index d5e0b25689..080d56f8e4 100644 --- a/apps/src/shared/gamma/containers/stack/StackContainer.tsx +++ b/apps/src/shared/gamma/containers/stack/StackContainer.tsx @@ -62,7 +62,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 +73,17 @@ export function StackContainer({ routeConfigs }: StackContainerProps) { setRouteOptions: navMethods.setRouteOptions, }; + const matchingConfig = routeConfigs.find( + config => config.name === name, + ); + if (!matchingConfig) { + throw new Error( + `[Stack] No config matches the "${name}" route name`, + ); + } + + const Component = matchingConfig.Component; + return ( & { activityMode: StackScreenProps['activityMode']; routeKey: StackScreenProps['screenKey']; isMarkedForDismissal: Boolean; // whether this route is during or after dismissal process 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 c9050a85d4..61e2ea0181 100644 --- a/apps/src/shared/gamma/containers/tabs/TabsContainer.tsx +++ b/apps/src/shared/gamma/containers/tabs/TabsContainer.tsx @@ -85,11 +85,11 @@ export function TabsContainer(props: TabsContainerProps) { route.routeKey === tabsNavState.suggestedState.selectedRouteKey; const matchingConfig = routeConfigs.find( - config => config.name === route.routeKey, + config => config.name === route.name, ); if (!matchingConfig) { throw new Error( - `[Tabs] None config matches the "${route.routeKey}" routeKey`, + `[Tabs] None config matches the "${route.name}" route name`, ); } From 9d9912e0ae9b7160bdd0391f90e3e93db252a37f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20Gaczo=C5=82?= Date: Fri, 24 Apr 2026 18:39:21 +0200 Subject: [PATCH 3/6] componentsByName added --- .../gamma/containers/stack/StackContainer.tsx | 16 ++++++++++------ .../gamma/containers/tabs/TabsContainer.tsx | 16 ++++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/apps/src/shared/gamma/containers/stack/StackContainer.tsx b/apps/src/shared/gamma/containers/stack/StackContainer.tsx index 080d56f8e4..537bb79c61 100644 --- a/apps/src/shared/gamma/containers/stack/StackContainer.tsx +++ b/apps/src/shared/gamma/containers/stack/StackContainer.tsx @@ -25,6 +25,14 @@ import { useParentNavigationEffect } from './hooks/useParentNavigationEffect'; export function StackContainer({ routeConfigs }: StackContainerProps) { useSanitizeRouteConfigs(routeConfigs); + const componentsByName = React.useMemo(() => { + const map = new Map(); + for (const config of routeConfigs) { + map.set(config.name, config.Component); + } + return map; + }, [routeConfigs]); + const [stackNavState, navActionDispatch]: [ StackNavigationState, React.Dispatch, @@ -73,17 +81,13 @@ export function StackContainer({ routeConfigs }: StackContainerProps) { setRouteOptions: navMethods.setRouteOptions, }; - const matchingConfig = routeConfigs.find( - config => config.name === name, - ); - if (!matchingConfig) { + const Component = componentsByName.get(name); + if (!Component) { throw new Error( `[Stack] No config matches the "${name}" route name`, ); } - const Component = matchingConfig.Component; - return ( { + const map = new Map(); + for (const config of routeConfigs) { + map.set(config.name, config.Component); + } + return map; + }, [routeConfigs]); + const [tabsNavState, dispatch]: [ TabsContainerState, React.Dispatch, @@ -84,17 +92,13 @@ export function TabsContainer(props: TabsContainerProps) { const pendingForUpdate = route.routeKey === tabsNavState.suggestedState.selectedRouteKey; - const matchingConfig = routeConfigs.find( - config => config.name === route.name, - ); - if (!matchingConfig) { + const Component = componentsByName.get(route.name); + if (!Component) { throw new Error( `[Tabs] None config matches the "${route.name}" route name`, ); } - const Component = matchingConfig.Component; - return ( Date: Mon, 27 Apr 2026 19:46:15 +0200 Subject: [PATCH 4/6] types update, StackContainer.types.tsx extension update to .ts --- .../{StackContainer.types.tsx => StackContainer.types.ts} | 2 +- .../shared/gamma/containers/tabs/TabsContainerItem.types.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename apps/src/shared/gamma/containers/stack/{StackContainer.types.tsx => StackContainer.types.ts} (98%) diff --git a/apps/src/shared/gamma/containers/stack/StackContainer.types.tsx b/apps/src/shared/gamma/containers/stack/StackContainer.types.ts similarity index 98% rename from apps/src/shared/gamma/containers/stack/StackContainer.types.tsx rename to apps/src/shared/gamma/containers/stack/StackContainer.types.ts index 303c5835b3..15cd96e543 100644 --- a/apps/src/shared/gamma/containers/stack/StackContainer.types.tsx +++ b/apps/src/shared/gamma/containers/stack/StackContainer.types.ts @@ -25,7 +25,7 @@ export type StackRouteConfig = { export type StackRoute = Omit & { 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/tabs/TabsContainerItem.types.ts b/apps/src/shared/gamma/containers/tabs/TabsContainerItem.types.ts index 0c0db001d1..8b5694eaae 100644 --- a/apps/src/shared/gamma/containers/tabs/TabsContainerItem.types.ts +++ b/apps/src/shared/gamma/containers/tabs/TabsContainerItem.types.ts @@ -1,4 +1,5 @@ import type { TabRoute, TabsNavigationMethods } from './TabsContainer.types'; +import React from 'react'; export type TabsContainerItemProps = { route: TabRoute; @@ -6,5 +7,4 @@ export type TabsContainerItemProps = { isSelected: boolean; pendingForUpdate: boolean; Component: React.ComponentType; -} - +}; From 88a0d86f2c110af05de23fea5d0d206e491512dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20Gaczo=C5=82?= Date: Mon, 27 Apr 2026 20:22:07 +0200 Subject: [PATCH 5/6] add useComponentsByName custom hook, shared among both containers --- .../containers/shared/use-components-by-name.ts | 17 +++++++++++++++++ .../gamma/containers/stack/StackContainer.tsx | 9 ++------- .../gamma/containers/tabs/TabsContainer.tsx | 9 ++------- 3 files changed, 21 insertions(+), 14 deletions(-) create mode 100644 apps/src/shared/gamma/containers/shared/use-components-by-name.ts 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 537bb79c61..950827ef7d 100644 --- a/apps/src/shared/gamma/containers/stack/StackContainer.tsx +++ b/apps/src/shared/gamma/containers/stack/StackContainer.tsx @@ -21,17 +21,12 @@ 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 = React.useMemo(() => { - const map = new Map(); - for (const config of routeConfigs) { - map.set(config.name, config.Component); - } - return map; - }, [routeConfigs]); + const componentsByName = useComponentsByName(routeConfigs); const [stackNavState, navActionDispatch]: [ StackNavigationState, diff --git a/apps/src/shared/gamma/containers/tabs/TabsContainer.tsx b/apps/src/shared/gamma/containers/tabs/TabsContainer.tsx index 7676d70593..d8ede4d2b0 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,13 +35,7 @@ export function TabsContainer(props: TabsContainerProps) { useSanitizeRouteConfigs(routeConfigs); - const componentsByName = React.useMemo(() => { - const map = new Map(); - for (const config of routeConfigs) { - map.set(config.name, config.Component); - } - return map; - }, [routeConfigs]); + const componentsByName = useComponentsByName(routeConfigs); const [tabsNavState, dispatch]: [ TabsContainerState, From 75b570c39d007a633edcfee826d8fe17f38f92c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20Gaczo=C5=82?= <167703022+sgaczol@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:52:21 +0200 Subject: [PATCH 6/6] comment fix Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- apps/src/shared/gamma/containers/tabs/TabsContainer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/shared/gamma/containers/tabs/TabsContainer.tsx b/apps/src/shared/gamma/containers/tabs/TabsContainer.tsx index d8ede4d2b0..958768392b 100644 --- a/apps/src/shared/gamma/containers/tabs/TabsContainer.tsx +++ b/apps/src/shared/gamma/containers/tabs/TabsContainer.tsx @@ -90,7 +90,7 @@ export function TabsContainer(props: TabsContainerProps) { const Component = componentsByName.get(route.name); if (!Component) { throw new Error( - `[Tabs] None config matches the "${route.name}" route name`, + `[Tabs] No route config matches the "${route.name}" route name`, ); }