From d85cae67d170a4c53a6f4924271b6e2773fc9e10 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Tue, 4 Nov 2025 01:02:42 +0300 Subject: [PATCH 1/5] fix current focused report bug for muliti tabs --- src/hooks/useCurrentReportID.tsx | 9 +-------- src/hooks/useSidebarOrderedReports.tsx | 2 +- src/libs/Navigation/AppNavigator/index.tsx | 14 +++++++++++++- src/libs/Navigation/NavigationRoot.tsx | 6 ------ 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/hooks/useCurrentReportID.tsx b/src/hooks/useCurrentReportID.tsx index 8a97c28c0333..4f7a5206eaea 100644 --- a/src/hooks/useCurrentReportID.tsx +++ b/src/hooks/useCurrentReportID.tsx @@ -1,14 +1,10 @@ import type {NavigationState} from '@react-navigation/native'; import React, {createContext, useCallback, useContext, useMemo, useState} from 'react'; import Navigation from '@libs/Navigation/Navigation'; -import {getReportIDFromLink} from '@libs/ReportUtils'; -import ONYXKEYS from '@src/ONYXKEYS'; -import useOnyx from './useOnyx'; type CurrentReportIDContextValue = { updateCurrentReportID: (state: NavigationState) => void; currentReportID: string | undefined; - currentReportIDFromPath: string | undefined; }; type CurrentReportIDContextProviderProps = { @@ -25,8 +21,6 @@ const CurrentReportIDContext = createContext function CurrentReportIDContextProvider(props: CurrentReportIDContextProviderProps) { const [currentReportID, setCurrentReportID] = useState(''); - const [lastVisitedPath] = useOnyx(ONYXKEYS.LAST_VISITED_PATH, {canBeMissing: true}); - const lastAccessReportFromPath = getReportIDFromLink(lastVisitedPath ?? null); /** * This function is used to update the currentReportID @@ -70,9 +64,8 @@ function CurrentReportIDContextProvider(props: CurrentReportIDContextProviderPro (): CurrentReportIDContextValue => ({ updateCurrentReportID, currentReportID, - currentReportIDFromPath: lastAccessReportFromPath || undefined, }), - [updateCurrentReportID, currentReportID, lastAccessReportFromPath], + [updateCurrentReportID, currentReportID], ); return {props.children}; diff --git a/src/hooks/useSidebarOrderedReports.tsx b/src/hooks/useSidebarOrderedReports.tsx index b139457b0eaa..2b64d0f9fe6c 100644 --- a/src/hooks/useSidebarOrderedReports.tsx +++ b/src/hooks/useSidebarOrderedReports.tsx @@ -78,7 +78,7 @@ function SidebarOrderedReportsContextProvider({ const {shouldUseNarrowLayout} = useResponsiveLayout(); const {accountID} = useCurrentUserPersonalDetails(); const currentReportIDValue = useCurrentReportID(); - const derivedCurrentReportID = currentReportIDForTests ?? currentReportIDValue?.currentReportIDFromPath ?? currentReportIDValue?.currentReportID; + const derivedCurrentReportID = currentReportIDForTests ?? currentReportIDValue?.currentReportID; const prevDerivedCurrentReportID = usePrevious(derivedCurrentReportID); const policyMemberAccountIDs = useMemo(() => getPolicyEmployeeListByIdWithoutCurrentUser(policies, undefined, accountID), [policies, accountID]); diff --git a/src/libs/Navigation/AppNavigator/index.tsx b/src/libs/Navigation/AppNavigator/index.tsx index 05961528ca11..a26656a91a18 100644 --- a/src/libs/Navigation/AppNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/index.tsx @@ -1,4 +1,6 @@ -import React, {lazy, memo, Suspense} from 'react'; +import {useNavigationState} from '@react-navigation/native'; +import React, {lazy, memo, Suspense, useEffect} from 'react'; +import useCurrentReportID from '@hooks/useCurrentReportID'; import lazyRetry from '@src/utils/lazyRetry'; const AuthScreens = lazy(() => lazyRetry(() => import('./AuthScreens'))); @@ -10,6 +12,16 @@ type AppNavigatorProps = { }; function AppNavigator({authenticated}: AppNavigatorProps) { + const currentReportIDValue = useCurrentReportID(); + const navState = useNavigationState((state) => state); + + useEffect(() => { + currentReportIDValue?.updateCurrentReportID(navState); + + // eslint-disable-next-line react-compiler/react-compiler + // eslint-disable-next-line react-hooks/exhaustive-deps -- we only want to run the effect on state change. + }, [navState]); + if (authenticated) { // These are the protected screens and only accessible when an authToken is present return ( diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index 8251044b728d..c0835199a5de 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -2,7 +2,6 @@ import type {NavigationState} from '@react-navigation/native'; import {DarkTheme, DefaultTheme, findFocusedRoute, getPathFromState, NavigationContainer} from '@react-navigation/native'; import React, {useCallback, useContext, useEffect, useMemo, useRef} from 'react'; import {ScrollOffsetContext} from '@components/ScrollOffsetContextProvider'; -import useCurrentReportID from '@hooks/useCurrentReportID'; import useOnyx from '@hooks/useOnyx'; import usePrevious from '@hooks/usePrevious'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -92,7 +91,6 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady}: N const theme = useTheme(); const {cleanStaleScrollOffsets} = useContext(ScrollOffsetContext); - const currentReportIDValue = useCurrentReportID(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true}); @@ -248,10 +246,6 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady}: N const currentRoute = navigationRef.getCurrentRoute(); Firebase.log(`[NAVIGATION] screen: ${currentRoute?.name}, params: ${JSON.stringify(currentRoute?.params ?? {})}`); - // Performance optimization to avoid context consumers to delay first render - setTimeout(() => { - currentReportIDValue?.updateCurrentReportID(state); - }, 0); parseAndLogRoute(state); // We want to clean saved scroll offsets for screens that aren't anymore in the state. From 87b5c093d07f12551acb495dd8a69f9fe07012c0 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Tue, 4 Nov 2025 19:37:44 +0300 Subject: [PATCH 2/5] update based on comments --- src/hooks/useCurrentReportID.tsx | 24 +++++++++++++++++----- src/libs/Navigation/AppNavigator/index.tsx | 8 ++++---- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/hooks/useCurrentReportID.tsx b/src/hooks/useCurrentReportID.tsx index 4f7a5206eaea..1cc2945fb04e 100644 --- a/src/hooks/useCurrentReportID.tsx +++ b/src/hooks/useCurrentReportID.tsx @@ -3,7 +3,8 @@ import React, {createContext, useCallback, useContext, useMemo, useState} from ' import Navigation from '@libs/Navigation/Navigation'; type CurrentReportIDContextValue = { - updateCurrentReportID: (state: NavigationState) => void; + getCurrentReportID: (state: NavigationState) => string | undefined; + updateCurrentReportID: (reportID: string | undefined) => void; currentReportID: string | undefined; }; @@ -23,10 +24,10 @@ function CurrentReportIDContextProvider(props: CurrentReportIDContextProviderPro const [currentReportID, setCurrentReportID] = useState(''); /** - * This function is used to update the currentReportID + * This function is used to get the currentReportID * @param state root navigation state */ - const updateCurrentReportID = useCallback( + const getCurrentReportID = useCallback( (state: NavigationState) => { const reportID = Navigation.getTopmostReportId(state); @@ -36,8 +37,20 @@ function CurrentReportIDContextProvider(props: CurrentReportIDContextProviderPro */ const params = state?.routes?.[state.index]?.params; if (params && 'screen' in params && typeof params.screen === 'string' && params.screen.indexOf('Settings_') !== -1) { - return; + return currentReportID; } + + return reportID; + }, + [currentReportID], + ); + + /** + * This function is used to update the currentReportID + * @param reportID + */ + const updateCurrentReportID = useCallback( + (reportID: string | undefined) => { // Prevent unnecessary updates when the report ID hasn't changed if (currentReportID === reportID) { return; @@ -63,9 +76,10 @@ function CurrentReportIDContextProvider(props: CurrentReportIDContextProviderPro const contextValue = useMemo( (): CurrentReportIDContextValue => ({ updateCurrentReportID, + getCurrentReportID, currentReportID, }), - [updateCurrentReportID, currentReportID], + [updateCurrentReportID, currentReportID, getCurrentReportID], ); return {props.children}; diff --git a/src/libs/Navigation/AppNavigator/index.tsx b/src/libs/Navigation/AppNavigator/index.tsx index a26656a91a18..b347106a5017 100644 --- a/src/libs/Navigation/AppNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/index.tsx @@ -13,14 +13,14 @@ type AppNavigatorProps = { function AppNavigator({authenticated}: AppNavigatorProps) { const currentReportIDValue = useCurrentReportID(); - const navState = useNavigationState((state) => state); + const currentReportID = useNavigationState((state) => currentReportIDValue?.getCurrentReportID(state)); useEffect(() => { - currentReportIDValue?.updateCurrentReportID(navState); + currentReportIDValue?.updateCurrentReportID(currentReportID); // eslint-disable-next-line react-compiler/react-compiler - // eslint-disable-next-line react-hooks/exhaustive-deps -- we only want to run the effect on state change. - }, [navState]); + // eslint-disable-next-line react-hooks/exhaustive-deps -- we only want to run the effect on currentReportID change. + }, [currentReportID]); if (authenticated) { // These are the protected screens and only accessible when an authToken is present From 29b9f54ad2b0b9c8a411bb09c601ebbfbb631cb7 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Tue, 4 Nov 2025 19:48:22 +0300 Subject: [PATCH 3/5] fix ts --- tests/unit/useCurrentReportIDTest.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/unit/useCurrentReportIDTest.tsx b/tests/unit/useCurrentReportIDTest.tsx index 5fa66d8381eb..022316b5fda6 100644 --- a/tests/unit/useCurrentReportIDTest.tsx +++ b/tests/unit/useCurrentReportIDTest.tsx @@ -71,7 +71,7 @@ describe('useCurrentReportID', () => { // When the updateCurrentReportID is called act(() => { - result.current?.updateCurrentReportID(navigationState); + result.current?.updateCurrentReportID(result.current.getCurrentReportID(navigationState)); }); // Then the currentReportID is updated @@ -81,7 +81,7 @@ describe('useCurrentReportID', () => { // When the updateCurrentReportID is called with the same reportID act(() => { - result.current?.updateCurrentReportID(navigationState); + result.current?.updateCurrentReportID(result.current.getCurrentReportID(navigationState)); }); // Then the setState should not be called again @@ -111,7 +111,7 @@ describe('useCurrentReportID', () => { // When the updateCurrentReportID is called act(() => { - result.current?.updateCurrentReportID(navigationState); + result.current?.updateCurrentReportID(result.current.getCurrentReportID(navigationState)); }); // Then the currentReportID is updated @@ -119,7 +119,7 @@ describe('useCurrentReportID', () => { // When the updateCurrentReportID is called with the same navigation state act(() => { - result.current?.updateCurrentReportID(navigationState); + result.current?.updateCurrentReportID(result.current.getCurrentReportID(navigationState)); }); // Then the setState should not be called again @@ -160,7 +160,7 @@ describe('useCurrentReportID', () => { // When the updateCurrentReportID is called act(() => { - result.current?.updateCurrentReportID(state1); + result.current?.updateCurrentReportID(result.current.getCurrentReportID(state1)); }); // Then the currentReportID is updated @@ -168,7 +168,7 @@ describe('useCurrentReportID', () => { // When the updateCurrentReportID is called with a different reportID act(() => { - result.current?.updateCurrentReportID(state2); + result.current?.updateCurrentReportID(result.current.getCurrentReportID(state2)); }); // Then the currentReportID is updated @@ -200,7 +200,7 @@ describe('useCurrentReportID', () => { // When the updateCurrentReportID is called act(() => { - result.current?.updateCurrentReportID(settingsState); + result.current?.updateCurrentReportID(result.current.getCurrentReportID(settingsState)); }); // Then the currentReportID should remain unchanged @@ -233,7 +233,7 @@ describe('useCurrentReportID', () => { // When the updateCurrentReportID is called act(() => { - result.current?.updateCurrentReportID(reportState); + result.current?.updateCurrentReportID(result.current.getCurrentReportID(reportState)); }); // Then the context value is updated From 6eae4f022f7783ed4ac7e47ff0b305aae4e0bc87 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 10 Dec 2025 18:03:20 +0300 Subject: [PATCH 4/5] Reverted unnecessary change --- src/hooks/useCurrentReportID.tsx | 31 +++++++++------------- src/libs/Navigation/AppNavigator/index.tsx | 14 +--------- src/libs/Navigation/NavigationRoot.tsx | 3 +++ tests/unit/useCurrentReportIDTest.tsx | 16 +++++------ 4 files changed, 24 insertions(+), 40 deletions(-) diff --git a/src/hooks/useCurrentReportID.tsx b/src/hooks/useCurrentReportID.tsx index 1cc2945fb04e..8a97c28c0333 100644 --- a/src/hooks/useCurrentReportID.tsx +++ b/src/hooks/useCurrentReportID.tsx @@ -1,11 +1,14 @@ import type {NavigationState} from '@react-navigation/native'; import React, {createContext, useCallback, useContext, useMemo, useState} from 'react'; import Navigation from '@libs/Navigation/Navigation'; +import {getReportIDFromLink} from '@libs/ReportUtils'; +import ONYXKEYS from '@src/ONYXKEYS'; +import useOnyx from './useOnyx'; type CurrentReportIDContextValue = { - getCurrentReportID: (state: NavigationState) => string | undefined; - updateCurrentReportID: (reportID: string | undefined) => void; + updateCurrentReportID: (state: NavigationState) => void; currentReportID: string | undefined; + currentReportIDFromPath: string | undefined; }; type CurrentReportIDContextProviderProps = { @@ -22,12 +25,14 @@ const CurrentReportIDContext = createContext function CurrentReportIDContextProvider(props: CurrentReportIDContextProviderProps) { const [currentReportID, setCurrentReportID] = useState(''); + const [lastVisitedPath] = useOnyx(ONYXKEYS.LAST_VISITED_PATH, {canBeMissing: true}); + const lastAccessReportFromPath = getReportIDFromLink(lastVisitedPath ?? null); /** - * This function is used to get the currentReportID + * This function is used to update the currentReportID * @param state root navigation state */ - const getCurrentReportID = useCallback( + const updateCurrentReportID = useCallback( (state: NavigationState) => { const reportID = Navigation.getTopmostReportId(state); @@ -37,20 +42,8 @@ function CurrentReportIDContextProvider(props: CurrentReportIDContextProviderPro */ const params = state?.routes?.[state.index]?.params; if (params && 'screen' in params && typeof params.screen === 'string' && params.screen.indexOf('Settings_') !== -1) { - return currentReportID; + return; } - - return reportID; - }, - [currentReportID], - ); - - /** - * This function is used to update the currentReportID - * @param reportID - */ - const updateCurrentReportID = useCallback( - (reportID: string | undefined) => { // Prevent unnecessary updates when the report ID hasn't changed if (currentReportID === reportID) { return; @@ -76,10 +69,10 @@ function CurrentReportIDContextProvider(props: CurrentReportIDContextProviderPro const contextValue = useMemo( (): CurrentReportIDContextValue => ({ updateCurrentReportID, - getCurrentReportID, currentReportID, + currentReportIDFromPath: lastAccessReportFromPath || undefined, }), - [updateCurrentReportID, currentReportID, getCurrentReportID], + [updateCurrentReportID, currentReportID, lastAccessReportFromPath], ); return {props.children}; diff --git a/src/libs/Navigation/AppNavigator/index.tsx b/src/libs/Navigation/AppNavigator/index.tsx index b347106a5017..05961528ca11 100644 --- a/src/libs/Navigation/AppNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/index.tsx @@ -1,6 +1,4 @@ -import {useNavigationState} from '@react-navigation/native'; -import React, {lazy, memo, Suspense, useEffect} from 'react'; -import useCurrentReportID from '@hooks/useCurrentReportID'; +import React, {lazy, memo, Suspense} from 'react'; import lazyRetry from '@src/utils/lazyRetry'; const AuthScreens = lazy(() => lazyRetry(() => import('./AuthScreens'))); @@ -12,16 +10,6 @@ type AppNavigatorProps = { }; function AppNavigator({authenticated}: AppNavigatorProps) { - const currentReportIDValue = useCurrentReportID(); - const currentReportID = useNavigationState((state) => currentReportIDValue?.getCurrentReportID(state)); - - useEffect(() => { - currentReportIDValue?.updateCurrentReportID(currentReportID); - - // eslint-disable-next-line react-compiler/react-compiler - // eslint-disable-next-line react-hooks/exhaustive-deps -- we only want to run the effect on currentReportID change. - }, [currentReportID]); - if (authenticated) { // These are the protected screens and only accessible when an authToken is present return ( diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index 4ee2172ec5aa..b8202e432b26 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -4,6 +4,7 @@ import {hasCompletedGuidedSetupFlowSelector, wasInvitedToNewDotSelector} from '@ import React, {useCallback, useContext, useEffect, useMemo, useRef} from 'react'; import {useOnboardingValues} from '@components/OnyxListItemProvider'; import {ScrollOffsetContext} from '@components/ScrollOffsetContextProvider'; +import useCurrentReportID from '@hooks/useCurrentReportID'; import useOnyx from '@hooks/useOnyx'; import usePrevious from '@hooks/usePrevious'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -92,6 +93,7 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady}: N const theme = useTheme(); const {cleanStaleScrollOffsets} = useContext(ScrollOffsetContext); + const currentReportIDValue = useCurrentReportID(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true}); @@ -248,6 +250,7 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady}: N const currentRoute = navigationRef.getCurrentRoute(); Firebase.log(`[NAVIGATION] screen: ${currentRoute?.name}, params: ${JSON.stringify(currentRoute?.params ?? {})}`); + currentReportIDValue?.updateCurrentReportID(state); parseAndLogRoute(state); // We want to clean saved scroll offsets for screens that aren't anymore in the state. diff --git a/tests/unit/useCurrentReportIDTest.tsx b/tests/unit/useCurrentReportIDTest.tsx index 022316b5fda6..5fa66d8381eb 100644 --- a/tests/unit/useCurrentReportIDTest.tsx +++ b/tests/unit/useCurrentReportIDTest.tsx @@ -71,7 +71,7 @@ describe('useCurrentReportID', () => { // When the updateCurrentReportID is called act(() => { - result.current?.updateCurrentReportID(result.current.getCurrentReportID(navigationState)); + result.current?.updateCurrentReportID(navigationState); }); // Then the currentReportID is updated @@ -81,7 +81,7 @@ describe('useCurrentReportID', () => { // When the updateCurrentReportID is called with the same reportID act(() => { - result.current?.updateCurrentReportID(result.current.getCurrentReportID(navigationState)); + result.current?.updateCurrentReportID(navigationState); }); // Then the setState should not be called again @@ -111,7 +111,7 @@ describe('useCurrentReportID', () => { // When the updateCurrentReportID is called act(() => { - result.current?.updateCurrentReportID(result.current.getCurrentReportID(navigationState)); + result.current?.updateCurrentReportID(navigationState); }); // Then the currentReportID is updated @@ -119,7 +119,7 @@ describe('useCurrentReportID', () => { // When the updateCurrentReportID is called with the same navigation state act(() => { - result.current?.updateCurrentReportID(result.current.getCurrentReportID(navigationState)); + result.current?.updateCurrentReportID(navigationState); }); // Then the setState should not be called again @@ -160,7 +160,7 @@ describe('useCurrentReportID', () => { // When the updateCurrentReportID is called act(() => { - result.current?.updateCurrentReportID(result.current.getCurrentReportID(state1)); + result.current?.updateCurrentReportID(state1); }); // Then the currentReportID is updated @@ -168,7 +168,7 @@ describe('useCurrentReportID', () => { // When the updateCurrentReportID is called with a different reportID act(() => { - result.current?.updateCurrentReportID(result.current.getCurrentReportID(state2)); + result.current?.updateCurrentReportID(state2); }); // Then the currentReportID is updated @@ -200,7 +200,7 @@ describe('useCurrentReportID', () => { // When the updateCurrentReportID is called act(() => { - result.current?.updateCurrentReportID(result.current.getCurrentReportID(settingsState)); + result.current?.updateCurrentReportID(settingsState); }); // Then the currentReportID should remain unchanged @@ -233,7 +233,7 @@ describe('useCurrentReportID', () => { // When the updateCurrentReportID is called act(() => { - result.current?.updateCurrentReportID(result.current.getCurrentReportID(reportState)); + result.current?.updateCurrentReportID(reportState); }); // Then the context value is updated From 4dc90df0380876c9e8039de608ae78116d60df9e Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 10 Dec 2025 18:04:53 +0300 Subject: [PATCH 5/5] remove unnecessary code --- src/hooks/useCurrentReportID.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/hooks/useCurrentReportID.tsx b/src/hooks/useCurrentReportID.tsx index 8a97c28c0333..4f7a5206eaea 100644 --- a/src/hooks/useCurrentReportID.tsx +++ b/src/hooks/useCurrentReportID.tsx @@ -1,14 +1,10 @@ import type {NavigationState} from '@react-navigation/native'; import React, {createContext, useCallback, useContext, useMemo, useState} from 'react'; import Navigation from '@libs/Navigation/Navigation'; -import {getReportIDFromLink} from '@libs/ReportUtils'; -import ONYXKEYS from '@src/ONYXKEYS'; -import useOnyx from './useOnyx'; type CurrentReportIDContextValue = { updateCurrentReportID: (state: NavigationState) => void; currentReportID: string | undefined; - currentReportIDFromPath: string | undefined; }; type CurrentReportIDContextProviderProps = { @@ -25,8 +21,6 @@ const CurrentReportIDContext = createContext function CurrentReportIDContextProvider(props: CurrentReportIDContextProviderProps) { const [currentReportID, setCurrentReportID] = useState(''); - const [lastVisitedPath] = useOnyx(ONYXKEYS.LAST_VISITED_PATH, {canBeMissing: true}); - const lastAccessReportFromPath = getReportIDFromLink(lastVisitedPath ?? null); /** * This function is used to update the currentReportID @@ -70,9 +64,8 @@ function CurrentReportIDContextProvider(props: CurrentReportIDContextProviderPro (): CurrentReportIDContextValue => ({ updateCurrentReportID, currentReportID, - currentReportIDFromPath: lastAccessReportFromPath || undefined, }), - [updateCurrentReportID, currentReportID, lastAccessReportFromPath], + [updateCurrentReportID, currentReportID], ); return {props.children};