From 52fbb2324d4bd43e9cf9f2e1dd62b458289b7d01 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 28 Jan 2026 11:07:55 +0100 Subject: [PATCH 1/5] Split WideRHPContext into State and Actions --- src/components/MoneyRequestHeader.tsx | 4 +- .../MoneyRequestReportTransactionList.tsx | 4 +- ...neyRequestReportTransactionsNavigation.tsx | 6 +- src/components/Navigation/TopBar.tsx | 6 +- .../ReportActionItem/MoneyRequestView.tsx | 4 +- src/components/Search/index.tsx | 6 +- .../Search/TransactionGroupListExpanded.tsx | 6 +- .../SidePanel/SidePanelModal/index.tsx | 6 +- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 4 +- .../WideRHPContextProvider/default.ts | 21 +++--- .../WideRHPContextProvider/index.native.tsx | 30 ++++++-- .../WideRHPContextProvider/index.tsx | 74 ++++++++++++------- .../WideRHPContextProvider/types.ts | 19 ++--- .../useShowSuperWideRHPVersion/index.ts | 6 +- .../useShowWideRHPVersion/index.ts | 6 +- .../WideRHPOverlayWrapper/index.tsx | 6 +- .../useResponsiveLayoutOnWideRHP/index.ts | 5 +- .../Navigation/AppNavigator/AuthScreens.tsx | 4 +- .../useModalStackScreenOptions.ts | 6 +- .../Navigators/RightModalNavigator.tsx | 10 ++- .../AppNavigator/useRHPScreenOptions.ts | 6 +- .../TransactionDuplicate/Confirmation.tsx | 6 +- .../ComposerWithSuggestions.tsx | 4 +- src/pages/iou/RejectReasonPage.tsx | 4 +- 24 files changed, 148 insertions(+), 105 deletions(-) diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index e947fd81b9a3..d3e17a7e9651 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -82,7 +82,7 @@ import type {MoneyRequestHeaderStatusBarProps} from './MoneyRequestHeaderStatusB import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar'; import MoneyRequestReportTransactionsNavigation from './MoneyRequestReportView/MoneyRequestReportTransactionsNavigation'; import {useSearchContext} from './Search/SearchContext'; -import {WideRHPContext} from './WideRHPContextProvider'; +import {useWideRHPState} from './WideRHPContextProvider'; type MoneyRequestHeaderProps = { /** The report currently being looked at */ @@ -171,7 +171,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre // If the parent report is a selfDM, it should always be opened in the Inbox tab const shouldOpenParentReportInCurrentTab = !isSelfDM(parentReport); - const {wideRHPRouteKeys} = useContext(WideRHPContext); + const {wideRHPRouteKeys} = useWideRHPState(); const [network] = useOnyx(ONYXKEYS.NETWORK, {canBeMissing: true}); const [quickAction] = useOnyx(ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE, {canBeMissing: true}); const [isSelfTourViewed = false] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {canBeMissing: true, selector: hasSeenTourSelector}); diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx index 4a9598349e7c..1a3dccffa81d 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx @@ -11,7 +11,7 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {useSearchContext} from '@components/Search/SearchContext'; import type {SortOrder} from '@components/Search/types'; import Text from '@components/Text'; -import {WideRHPContext} from '@components/WideRHPContextProvider'; +import {useWideRHPActions} from '@components/WideRHPContextProvider'; import useCopySelectionHelper from '@hooks/useCopySelectionHelper'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useHandleSelectionMode from '@hooks/useHandleSelectionMode'; @@ -171,7 +171,7 @@ function MoneyRequestReportTransactionList({ // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth const {isSmallScreenWidth, isMediumScreenWidth} = useResponsiveLayout(); const {shouldUseNarrowLayout} = useResponsiveLayoutOnWideRHP(); - const {markReportIDAsExpense} = useContext(WideRHPContext); + const {markReportIDAsExpense} = useWideRHPActions(); const [isModalVisible, setIsModalVisible] = useState(false); const [selectedTransactionID, setSelectedTransactionID] = useState(''); const {reportPendingAction} = getReportOfflinePendingActionAndErrors(report); diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx index d8438a3bf253..401714f0a0bf 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx @@ -1,9 +1,9 @@ import {findFocusedRoute} from '@react-navigation/native'; -import React, {useCallback, useContext, useEffect, useMemo} from 'react'; +import React, {useCallback, useEffect, useMemo} from 'react'; import type {GestureResponderEvent} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import PrevNextButtons from '@components/PrevNextButtons'; -import {WideRHPContext} from '@components/WideRHPContextProvider'; +import {useWideRHPActions} from '@components/WideRHPContextProvider'; import useOnyx from '@hooks/useOnyx'; import {createTransactionThreadReport, setOptimisticTransactionThread} from '@libs/actions/Report'; import {clearActiveTransactionIDs} from '@libs/actions/TransactionThreadNavigation'; @@ -38,7 +38,7 @@ function MoneyRequestReportTransactionsNavigation({currentTransactionID, isFromR canBeMissing: true, }); - const {markReportIDAsExpense} = useContext(WideRHPContext); + const {markReportIDAsExpense} = useWideRHPActions(); const {prevTransactionID, nextTransactionID} = useMemo(() => { if (!transactionIDsList || transactionIDsList.length < 2) { diff --git a/src/components/Navigation/TopBar.tsx b/src/components/Navigation/TopBar.tsx index 71e7a9aa5f50..492196201e9d 100644 --- a/src/components/Navigation/TopBar.tsx +++ b/src/components/Navigation/TopBar.tsx @@ -1,4 +1,4 @@ -import React, {useContext} from 'react'; +import React from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import LoadingBar from '@components/LoadingBar'; @@ -6,7 +6,7 @@ import {PressableWithoutFeedback} from '@components/Pressable'; import SearchButton from '@components/Search/SearchRouter/SearchButton'; import SidePanelButton from '@components/SidePanel/SidePanelButton'; import Text from '@components/Text'; -import {WideRHPContext} from '@components/WideRHPContextProvider'; +import {useWideRHPState} from '@components/WideRHPContextProvider'; import useLoadingBarVisibility from '@hooks/useLoadingBarVisibility'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -34,7 +34,7 @@ function TopBar({breadcrumbLabel, shouldDisplaySearch = true, shouldDisplayHelpB const shouldShowLoadingBarForReports = useLoadingBarVisibility(); const isAnonymousUser = isAnonymousUserUtil(session); - const {wideRHPRouteKeys} = useContext(WideRHPContext); + const {wideRHPRouteKeys} = useWideRHPState(); const isWideRHPVisible = !!wideRHPRouteKeys.length; const displaySignIn = isAnonymousUser; diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 08e907aa72bd..594e15939959 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -13,7 +13,7 @@ import {useSearchContext} from '@components/Search/SearchContext'; import Switch from '@components/Switch'; import Text from '@components/Text'; import ViolationMessages from '@components/ViolationMessages'; -import {WideRHPContext} from '@components/WideRHPContextProvider'; +import {useWideRHPState} from '@components/WideRHPContextProvider'; import useActiveRoute from '@hooks/useActiveRoute'; import useCurrencyList from '@hooks/useCurrencyList'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; @@ -803,7 +803,7 @@ function MoneyRequestView({ // In this case we want to use this value. The shouldUseNarrowLayout will always be true as this case is handled when we display ReportScreen in RHP. // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth const {isSmallScreenWidth} = useResponsiveLayout(); - const {wideRHPRouteKeys} = useContext(WideRHPContext); + const {wideRHPRouteKeys} = useWideRHPState(); // If the view is readonly, we don't need the transactionThread dependency if ((!readonly && !transactionThreadReport?.reportID) || !transaction?.transactionID) { diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index b08800ec89c4..428b6e262458 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -11,7 +11,7 @@ import ConfirmModal from '@components/ConfirmModal'; import SearchTableHeader from '@components/SelectionListWithSections/SearchTableHeader'; import type {ReportActionListItemType, SearchListItem, SelectionListHandle, TransactionGroupListItemType, TransactionListItemType} from '@components/SelectionListWithSections/types'; import SearchRowSkeleton from '@components/Skeletons/SearchRowSkeleton'; -import {WideRHPContext} from '@components/WideRHPContextProvider'; +import {useWideRHPActions} from '@components/WideRHPContextProvider'; import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import useCardFeedsForDisplay from '@hooks/useCardFeedsForDisplay'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; @@ -225,7 +225,7 @@ function Search({ const {isSmallScreenWidth, isLargeScreenWidth} = useResponsiveLayout(); const navigation = useNavigation>(); const isFocused = useIsFocused(); - const {markReportIDAsExpense} = useContext(WideRHPContext); + const {markReportIDAsExpense} = useWideRHPActions(); const { currentSearchHash, setCurrentSearchHashAndKey, @@ -257,7 +257,7 @@ function Search({ const [customCardNames] = useOnyx(ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES, {canBeMissing: true}); const isExpenseReportType = type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT; - const {markReportIDAsMultiTransactionExpense, unmarkReportIDAsMultiTransactionExpense} = useContext(WideRHPContext); + const {markReportIDAsMultiTransactionExpense, unmarkReportIDAsMultiTransactionExpense} = useWideRHPActions(); const archivedReportsIdSet = useArchivedReportsIdSet(); diff --git a/src/components/SelectionListWithSections/Search/TransactionGroupListExpanded.tsx b/src/components/SelectionListWithSections/Search/TransactionGroupListExpanded.tsx index 874021d576e2..e17033f568c9 100644 --- a/src/components/SelectionListWithSections/Search/TransactionGroupListExpanded.tsx +++ b/src/components/SelectionListWithSections/Search/TransactionGroupListExpanded.tsx @@ -1,4 +1,4 @@ -import React, {useContext} from 'react'; +import React from 'react'; import {View} from 'react-native'; import ActivityIndicator from '@components/ActivityIndicator'; import Button from '@components/Button'; @@ -9,7 +9,7 @@ import SearchTableHeader from '@components/SelectionListWithSections/SearchTable import type {ListItem, TransactionGroupListExpandedProps, TransactionListItemType} from '@components/SelectionListWithSections/types'; import Text from '@components/Text'; import TransactionItemRow from '@components/TransactionItemRow'; -import {WideRHPContext} from '@components/WideRHPContextProvider'; +import {useWideRHPActions} from '@components/WideRHPContextProvider'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -87,7 +87,7 @@ function TransactionGroupListExpanded({ const taxAmountColumnSize = isTaxAmountColumnWide ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL; const dateColumnSize = shouldShowYearForSomeTransaction ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL; - const {markReportIDAsExpense} = useContext(WideRHPContext); + const {markReportIDAsExpense} = useWideRHPActions(); const openReportInRHP = (transactionItem: TransactionListItemType) => { const backTo = Navigation.getActiveRoute(); const reportID = getReportIDForTransaction(transactionItem, transactionItem?.reportAction?.childReportID); diff --git a/src/components/SidePanel/SidePanelModal/index.tsx b/src/components/SidePanel/SidePanelModal/index.tsx index 60fb40444e7c..a85af7a0bb1a 100644 --- a/src/components/SidePanel/SidePanelModal/index.tsx +++ b/src/components/SidePanel/SidePanelModal/index.tsx @@ -1,5 +1,5 @@ import {isRHPVisibleSelector} from '@selectors/Modal'; -import React, {useContext, useEffect} from 'react'; +import React, {useEffect} from 'react'; // eslint-disable-next-line no-restricted-imports import {Animated, View} from 'react-native'; // @ts-expect-error This is a workaround to display SidePanel on top of everything, @@ -8,7 +8,7 @@ import ModalPortal from 'react-native-web/dist/exports/Modal/ModalPortal'; import ColorSchemeWrapper from '@components/ColorSchemeWrapper'; import FocusTrapForModal from '@components/FocusTrap/FocusTrapForModal'; import SidePanelOverlay from '@components/SidePanel/SidePanelOverlay'; -import {WideRHPContext} from '@components/WideRHPContextProvider'; +import {useWideRHPState} from '@components/WideRHPContextProvider'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; import useOnyx from '@hooks/useOnyx'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -27,7 +27,7 @@ function SidePanelModal({children, sidePanelTranslateX, closeSidePanel, shouldHi const [isRHPVisible = false] = useOnyx(ONYXKEYS.MODAL, {selector: isRHPVisibleSelector, canBeMissing: true}); const uniqueModalId = ComposerFocusManager.getId(); - const {wideRHPRouteKeys, isWideRHPFocused, superWideRHPRouteKeys, isSuperWideRHPFocused} = useContext(WideRHPContext); + const {wideRHPRouteKeys, isWideRHPFocused, superWideRHPRouteKeys, isSuperWideRHPFocused} = useWideRHPState(); const shouldOverlayBeVisible = (!!wideRHPRouteKeys.length && isWideRHPFocused) || (!!superWideRHPRouteKeys.length && isSuperWideRHPFocused) || !isRHPVisible; diff --git a/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx index 0c8e725f30f0..b7975b9b9e8a 100644 --- a/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -12,7 +12,7 @@ import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import Text from '@components/Text'; import ValidateCodeCountdown from '@components/ValidateCodeCountdown'; import type {ValidateCodeCountdownHandle} from '@components/ValidateCodeCountdown/types'; -import {WideRHPContext} from '@components/WideRHPContextProvider'; +import {useWideRHPState} from '@components/WideRHPContextProvider'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; @@ -114,7 +114,7 @@ function BaseValidateCodeForm({ }: ValidateCodeFormProps) { const {translate} = useLocalize(); const {isOffline} = useNetwork(); - const {wideRHPRouteKeys} = useContext(WideRHPContext); + const {wideRHPRouteKeys} = useWideRHPState(); const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); diff --git a/src/components/WideRHPContextProvider/default.ts b/src/components/WideRHPContextProvider/default.ts index 10a359bfcb9c..ab34f0466ece 100644 --- a/src/components/WideRHPContextProvider/default.ts +++ b/src/components/WideRHPContextProvider/default.ts @@ -1,27 +1,30 @@ -import type {WideRHPContextType} from './types'; +import type {WideRHPActionsContextType, WideRHPStateContextType} from './types'; -const defaultWideRHPContextValue: WideRHPContextType = { +const defaultWideRHPStateContextValue: WideRHPStateContextType = { wideRHPRouteKeys: [], + superWideRHPRouteKeys: [], shouldRenderSecondaryOverlayForWideRHP: false, shouldRenderSecondaryOverlayForRHPOnWideRHP: false, shouldRenderSecondaryOverlayForRHPOnSuperWideRHP: false, + shouldRenderTertiaryOverlay: false, + isWideRHPFocused: false, + isSuperWideRHPFocused: false, +}; + +const defaultWideRHPActionsContextValue: WideRHPActionsContextType = { showWideRHPVersion: () => {}, + showSuperWideRHPVersion: () => {}, removeWideRHPRouteKey: () => {}, + removeSuperWideRHPRouteKey: () => {}, markReportIDAsExpense: () => {}, markReportIDAsMultiTransactionExpense: () => {}, unmarkReportIDAsMultiTransactionExpense: () => {}, isReportIDMarkedAsExpense: () => false, isReportIDMarkedAsMultiTransactionExpense: () => false, - isWideRHPFocused: false, - isSuperWideRHPFocused: false, - shouldRenderTertiaryOverlay: false, - superWideRHPRouteKeys: [], - showSuperWideRHPVersion: () => {}, - removeSuperWideRHPRouteKey: () => {}, syncRHPKeys: () => {}, clearWideRHPKeys: () => {}, setIsWideRHPClosing: () => {}, setIsSuperWideRHPClosing: () => {}, }; -export default defaultWideRHPContextValue; +export {defaultWideRHPStateContextValue, defaultWideRHPActionsContextValue}; diff --git a/src/components/WideRHPContextProvider/index.native.tsx b/src/components/WideRHPContextProvider/index.native.tsx index b8773852e8dd..081700eeb986 100644 --- a/src/components/WideRHPContextProvider/index.native.tsx +++ b/src/components/WideRHPContextProvider/index.native.tsx @@ -1,10 +1,10 @@ -import React, {createContext} from 'react'; +import React, {createContext, useContext} from 'react'; // We use Animated for all functionality related to wide RHP to make it easier // to interact with react-navigation components (e.g., CardContainer, interpolator), which also use Animated. // eslint-disable-next-line no-restricted-imports import {Animated} from 'react-native'; -import defaultWideRHPContextValue from './default'; -import type {WideRHPContextType} from './types'; +import {defaultWideRHPActionsContextValue, defaultWideRHPStateContextValue} from './default'; +import type {WideRHPActionsContextType, WideRHPStateContextType} from './types'; const secondOverlayWideRHPProgress = new Animated.Value(0); const secondOverlayRHPOnWideRHPProgress = new Animated.Value(0); @@ -20,10 +20,23 @@ const modalStackOverlayWideRHPPositionLeft = new Animated.Value(0); const expandedRHPProgress = new Animated.Value(0); -const WideRHPContext = createContext(defaultWideRHPContextValue); +const WideRHPStateContext = createContext(defaultWideRHPStateContextValue); +const WideRHPActionsContext = createContext(defaultWideRHPActionsContextValue); function WideRHPContextProvider({children}: React.PropsWithChildren) { - return {children}; + return ( + + {children} + + ); +} + +function useWideRHPState() { + return useContext(WideRHPStateContext); +} + +function useWideRHPActions() { + return useContext(WideRHPActionsContext); } export default WideRHPContextProvider; @@ -38,6 +51,9 @@ export { secondOverlayRHPOnWideRHPProgress, secondOverlayWideRHPProgress, thirdOverlayProgress, - WideRHPContext, + WideRHPStateContext, + WideRHPActionsContext, + useWideRHPState, + useWideRHPActions, }; -export type {WideRHPContextType}; +export type {WideRHPStateContextType, WideRHPActionsContextType}; diff --git a/src/components/WideRHPContextProvider/index.tsx b/src/components/WideRHPContextProvider/index.tsx index c697c1a846b4..438d5e31c950 100644 --- a/src/components/WideRHPContextProvider/index.tsx +++ b/src/components/WideRHPContextProvider/index.tsx @@ -1,5 +1,5 @@ import {findFocusedRoute} from '@react-navigation/native'; -import React, {createContext, useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import React, {createContext, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; // We use Animated for all functionality related to wide RHP to make it easier // to interact with react-navigation components (e.g., CardContainer, interpolator), which also use Animated. // eslint-disable-next-line no-restricted-imports @@ -15,10 +15,10 @@ import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Report} from '@src/types/onyx'; -import defaultWideRHPContextValue from './default'; +import {defaultWideRHPActionsContextValue, defaultWideRHPStateContextValue} from './default'; import getIsRHPDisplayedBelow from './getIsRHPDisplayedBelow'; import getVisibleRHPKeys from './getVisibleRHPRouteKeys'; -import type {WideRHPContextType} from './types'; +import type {WideRHPActionsContextType, WideRHPStateContextType} from './types'; import useShouldRenderOverlay from './useShouldRenderOverlay'; // 0 is folded/hidden, 1 is expanded/shown @@ -45,7 +45,8 @@ const animatedWideRHPWidth = new Animated.Value(wideRHPWidth); const modalStackOverlayWideRHPPositionLeft = new Animated.Value(superWideRHPWidth - wideRHPWidth); const modalStackOverlaySuperWideRHPPositionLeft = new Animated.Value(superWideRHPWidth - singleRHPWidth); -const WideRHPContext = createContext(defaultWideRHPContextValue); +const WideRHPStateContext = createContext(defaultWideRHPStateContextValue); +const WideRHPActionsContext = createContext(defaultWideRHPActionsContextValue); const expenseReportSelector = (reports: OnyxCollection) => { return Object.fromEntries( @@ -133,13 +134,13 @@ function WideRHPContextProvider({children}: React.PropsWithChildren) { }; }); - const isWideRHPFocused = useMemo(() => { + const isWideRHPFocused = (() => { return !!focusedRoute?.key && allWideRHPRouteKeys.includes(focusedRoute.key); - }, [focusedRoute?.key, allWideRHPRouteKeys]); + })(); - const isSuperWideRHPFocused = useMemo(() => { + const isSuperWideRHPFocused = (() => { return !!focusedRoute?.key && allSuperWideRHPRouteKeys.includes(focusedRoute.key); - }, [focusedRoute?.key, allSuperWideRHPRouteKeys]); + })(); const isRHPFocused = focusedNavigator === NAVIGATORS.RIGHT_MODAL_NAVIGATOR; @@ -322,49 +323,55 @@ function WideRHPContextProvider({children}: React.PropsWithChildren) { return () => subscription?.remove(); }, []); - const value = useMemo( + const stateValue = useMemo( () => ({ - expandedRHPProgress, wideRHPRouteKeys, superWideRHPRouteKeys, - showWideRHPVersion, - showSuperWideRHPVersion, - removeWideRHPRouteKey, - removeSuperWideRHPRouteKey, shouldRenderSecondaryOverlayForRHPOnSuperWideRHP, shouldRenderSecondaryOverlayForRHPOnWideRHP, shouldRenderSecondaryOverlayForWideRHP, shouldRenderTertiaryOverlay, + isWideRHPFocused, + isSuperWideRHPFocused, + }), + [ + wideRHPRouteKeys, + superWideRHPRouteKeys, + shouldRenderSecondaryOverlayForRHPOnSuperWideRHP, + shouldRenderSecondaryOverlayForRHPOnWideRHP, + shouldRenderSecondaryOverlayForWideRHP, + shouldRenderTertiaryOverlay, + isWideRHPFocused, + isSuperWideRHPFocused, + ], + ); + + const actionsValue = useMemo( + () => ({ + showWideRHPVersion, + showSuperWideRHPVersion, + removeWideRHPRouteKey, + removeSuperWideRHPRouteKey, markReportIDAsExpense, markReportIDAsMultiTransactionExpense, unmarkReportIDAsMultiTransactionExpense, isReportIDMarkedAsExpense, isReportIDMarkedAsMultiTransactionExpense, - isWideRHPFocused, - isSuperWideRHPFocused, syncRHPKeys, clearWideRHPKeys, setIsWideRHPClosing, setIsSuperWideRHPClosing, }), [ - wideRHPRouteKeys, - superWideRHPRouteKeys, showWideRHPVersion, showSuperWideRHPVersion, removeWideRHPRouteKey, removeSuperWideRHPRouteKey, - shouldRenderSecondaryOverlayForRHPOnSuperWideRHP, - shouldRenderSecondaryOverlayForRHPOnWideRHP, - shouldRenderSecondaryOverlayForWideRHP, - shouldRenderTertiaryOverlay, markReportIDAsExpense, markReportIDAsMultiTransactionExpense, unmarkReportIDAsMultiTransactionExpense, isReportIDMarkedAsExpense, isReportIDMarkedAsMultiTransactionExpense, - isWideRHPFocused, - isSuperWideRHPFocused, syncRHPKeys, clearWideRHPKeys, setIsWideRHPClosing, @@ -372,7 +379,19 @@ function WideRHPContextProvider({children}: React.PropsWithChildren) { ], ); - return {children}; + return ( + + {children} + + ); +} + +function useWideRHPState() { + return useContext(WideRHPStateContext); +} + +function useWideRHPActions() { + return useContext(WideRHPActionsContext); } export default WideRHPContextProvider; @@ -388,5 +407,8 @@ export { secondOverlayRHPOnWideRHPProgress, secondOverlayRHPOnSuperWideRHPProgress, thirdOverlayProgress, - WideRHPContext, + WideRHPStateContext, + WideRHPActionsContext, + useWideRHPState, + useWideRHPActions, }; diff --git a/src/components/WideRHPContextProvider/types.ts b/src/components/WideRHPContextProvider/types.ts index 20300f48287e..91e0ed9494a4 100644 --- a/src/components/WideRHPContextProvider/types.ts +++ b/src/components/WideRHPContextProvider/types.ts @@ -1,6 +1,6 @@ import type {NavigationRoute} from '@libs/Navigation/types'; -type WideRHPContextType = { +type WideRHPStateContextType = { // Route keys of screens that should be displayed in wide format wideRHPRouteKeys: string[]; @@ -19,6 +19,14 @@ type WideRHPContextType = { // If the tertiary overlay should be rendered. This value takes into account the delay of closing transition. shouldRenderTertiaryOverlay: boolean; + // Whether the currently focused route is inside the wide RHP set + isWideRHPFocused: boolean; + + // Whether the currently focused route is inside the super wide RHP set + isSuperWideRHPFocused: boolean; +}; + +type WideRHPActionsContextType = { // Show given route as in wide format showWideRHPVersion: (route: NavigationRoute) => void; @@ -46,12 +54,6 @@ type WideRHPContextType = { // Check if reportID is marked as multi-transaction expense isReportIDMarkedAsMultiTransactionExpense: (reportID: string) => boolean; - // Whether the currently focused route is inside the wide RHP set - isWideRHPFocused: boolean; - - // Whether the currently focused route is inside the super wide RHP set - isSuperWideRHPFocused: boolean; - // Sync super wide and wide RHP keys with the visible RHP screens syncRHPKeys: () => void; @@ -65,5 +67,4 @@ type WideRHPContextType = { setIsSuperWideRHPClosing: (isClosing: boolean) => void; }; -// eslint-disable-next-line import/prefer-default-export -export type {WideRHPContextType}; +export type {WideRHPStateContextType, WideRHPActionsContextType}; diff --git a/src/components/WideRHPContextProvider/useShowSuperWideRHPVersion/index.ts b/src/components/WideRHPContextProvider/useShowSuperWideRHPVersion/index.ts index 2b5853f7c613..89c000febd84 100644 --- a/src/components/WideRHPContextProvider/useShowSuperWideRHPVersion/index.ts +++ b/src/components/WideRHPContextProvider/useShowSuperWideRHPVersion/index.ts @@ -1,8 +1,8 @@ import {useRoute} from '@react-navigation/native'; -import {useCallback, useContext, useEffect} from 'react'; +import {useCallback, useEffect} from 'react'; import {navigationRef} from '@libs/Navigation/Navigation'; import NAVIGATORS from '@src/NAVIGATORS'; -import {expandedRHPProgress, WideRHPContext} from '..'; +import {expandedRHPProgress, useWideRHPActions} from '..'; /** * Hook that manages super wide RHP display for a screen based on condition or optimistic state. @@ -22,7 +22,7 @@ function useShowSuperWideRHPVersion(condition: boolean) { removeSuperWideRHPRouteKey, isReportIDMarkedAsExpense, isReportIDMarkedAsMultiTransactionExpense, - } = useContext(WideRHPContext); + } = useWideRHPActions(); const onSuperWideRHPClose = useCallback(() => { removeWideRHPRouteKey(route); diff --git a/src/components/WideRHPContextProvider/useShowWideRHPVersion/index.ts b/src/components/WideRHPContextProvider/useShowWideRHPVersion/index.ts index 4adf2fef8f4b..5e5bde4b0981 100644 --- a/src/components/WideRHPContextProvider/useShowWideRHPVersion/index.ts +++ b/src/components/WideRHPContextProvider/useShowWideRHPVersion/index.ts @@ -1,8 +1,8 @@ import {useRoute} from '@react-navigation/native'; -import {useCallback, useContext, useEffect} from 'react'; +import {useCallback, useEffect} from 'react'; import {navigationRef} from '@libs/Navigation/Navigation'; import NAVIGATORS from '@src/NAVIGATORS'; -import {expandedRHPProgress, WideRHPContext} from '..'; +import {expandedRHPProgress, useWideRHPActions} from '..'; /** * Hook that manages wide RHP display for a screen based on condition or optimistic state. @@ -14,7 +14,7 @@ import {expandedRHPProgress, WideRHPContext} from '..'; function useShowWideRHPVersion(condition: boolean) { const route = useRoute(); const reportID = route.params && 'reportID' in route.params && typeof route.params.reportID === 'string' ? route.params.reportID : ''; - const {showWideRHPVersion, removeWideRHPRouteKey, isReportIDMarkedAsExpense} = useContext(WideRHPContext); + const {showWideRHPVersion, removeWideRHPRouteKey, isReportIDMarkedAsExpense} = useWideRHPActions(); const onWideRHPClose = useCallback(() => { removeWideRHPRouteKey(route); diff --git a/src/components/WideRHPOverlayWrapper/index.tsx b/src/components/WideRHPOverlayWrapper/index.tsx index f70c91495f9c..da63e02b7a35 100644 --- a/src/components/WideRHPOverlayWrapper/index.tsx +++ b/src/components/WideRHPOverlayWrapper/index.tsx @@ -1,5 +1,5 @@ import {useRoute} from '@react-navigation/native'; -import React, {useContext} from 'react'; +import React from 'react'; import { animatedReceiptPaneRHPWidth, modalStackOverlaySuperWideRHPPositionLeft, @@ -7,14 +7,14 @@ import { secondOverlayRHPOnSuperWideRHPProgress, secondOverlayRHPOnWideRHPProgress, secondOverlayWideRHPProgress, - WideRHPContext, + useWideRHPState, } from '@components/WideRHPContextProvider'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import Overlay from '@libs/Navigation/AppNavigator/Navigators/Overlay'; function SecondaryOverlay() { const {shouldRenderSecondaryOverlayForRHPOnSuperWideRHP, shouldRenderSecondaryOverlayForRHPOnWideRHP, shouldRenderSecondaryOverlayForWideRHP, superWideRHPRouteKeys, wideRHPRouteKeys} = - useContext(WideRHPContext); + useWideRHPState(); const route = useRoute(); diff --git a/src/hooks/useResponsiveLayoutOnWideRHP/index.ts b/src/hooks/useResponsiveLayoutOnWideRHP/index.ts index 8919d15bdfe6..f7328d7708c4 100644 --- a/src/hooks/useResponsiveLayoutOnWideRHP/index.ts +++ b/src/hooks/useResponsiveLayoutOnWideRHP/index.ts @@ -1,6 +1,5 @@ import {useRoute} from '@react-navigation/native'; -import {useContext} from 'react'; -import {WideRHPContext} from '@components/WideRHPContextProvider'; +import {useWideRHPState} from '@components/WideRHPContextProvider'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import type ResponsiveLayoutOnWideRHPResult from './types'; @@ -16,7 +15,7 @@ export default function useResponsiveLayoutOnWideRHP(): ResponsiveLayoutOnWideRH // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth const {isSmallScreenWidth, isInNarrowPaneModal} = responsiveLayoutValues; - const {superWideRHPRouteKeys, wideRHPRouteKeys} = useContext(WideRHPContext); + const {superWideRHPRouteKeys, wideRHPRouteKeys} = useWideRHPState(); const isWideRHPDisplayedOnWideLayout = !isSmallScreenWidth && wideRHPRouteKeys.includes(route?.key); diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 17ccde594eb0..e70189dd1d13 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -16,7 +16,7 @@ import {SearchContextProvider} from '@components/Search/SearchContext'; import {useSearchRouterActions} from '@components/Search/SearchRouter/SearchRouterContext'; import SearchRouterModal from '@components/Search/SearchRouter/SearchRouterModal'; import SupportalPermissionDeniedModalProvider from '@components/SupportalPermissionDeniedModalProvider'; -import {WideRHPContext} from '@components/WideRHPContextProvider'; +import {useWideRHPState} from '@components/WideRHPContextProvider'; import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import useAutoUpdateTimezone from '@hooks/useAutoUpdateTimezone'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; @@ -157,7 +157,7 @@ function AuthScreens() { const modalCardStyleInterpolator = useModalCardStyleInterpolator(); const archivedReportsIdSet = useArchivedReportsIdSet(); const {shouldRenderSecondaryOverlayForWideRHP, shouldRenderSecondaryOverlayForRHPOnWideRHP, shouldRenderSecondaryOverlayForRHPOnSuperWideRHP, shouldRenderTertiaryOverlay} = - useContext(WideRHPContext); + useWideRHPState(); // Check if the user is currently on a 2FA setup screen // We can't rely on useRoute in this component because we're not a child of a Navigator, so we must sift through nav state by hand diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/useModalStackScreenOptions.ts b/src/libs/Navigation/AppNavigator/ModalStackNavigators/useModalStackScreenOptions.ts index 685e72b3fb5a..8dcdcdc0ac55 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/useModalStackScreenOptions.ts +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/useModalStackScreenOptions.ts @@ -1,7 +1,7 @@ import type {ParamListBase} from '@react-navigation/native'; import {CardStyleInterpolators} from '@react-navigation/stack'; -import {useCallback, useContext} from 'react'; -import {WideRHPContext} from '@components/WideRHPContextProvider'; +import {useCallback} from 'react'; +import {useWideRHPState} from '@components/WideRHPContextProvider'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import enhanceCardStyleInterpolator from '@libs/Navigation/AppNavigator/enhanceCardStyleInterpolator'; @@ -16,7 +16,7 @@ function useWideModalStackScreenOptions() { // https://github.com/Expensify/App/issues/63747 // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth const {isSmallScreenWidth} = useResponsiveLayout(); - const {wideRHPRouteKeys, superWideRHPRouteKeys} = useContext(WideRHPContext); + const {wideRHPRouteKeys, superWideRHPRouteKeys} = useWideRHPState(); return useCallback<({route}: {route: PlatformStackRouteProp}) => PlatformStackNavigationOptions>( ({route}) => { diff --git a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx index ab6222ef13c4..70ea67897227 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx @@ -1,6 +1,6 @@ import type {NavigatorScreenParams} from '@react-navigation/native'; import {useFocusEffect} from '@react-navigation/native'; -import React, {useCallback, useContext, useEffect, useMemo, useRef} from 'react'; +import React, {useCallback, useEffect, useMemo, useRef} from 'react'; // eslint-disable-next-line no-restricted-imports import {Animated, DeviceEventEmitter, InteractionManager} from 'react-native'; import NoDropZone from '@components/DragAndDrop/NoDropZone'; @@ -11,7 +11,8 @@ import { secondOverlayRHPOnWideRHPProgress, secondOverlayWideRHPProgress, thirdOverlayProgress, - WideRHPContext, + useWideRHPActions, + useWideRHPState, } from '@components/WideRHPContextProvider'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSidePanel from '@hooks/useSidePanel'; @@ -47,7 +48,7 @@ const singleRHPWidth = variables.sideBarWidth; const getWideRHPWidth = (windowWidth: number) => variables.sideBarWidth + calculateReceiptPaneRHPWidth(windowWidth); function SecondaryOverlay() { - const {shouldRenderSecondaryOverlayForWideRHP, shouldRenderSecondaryOverlayForRHPOnWideRHP, shouldRenderSecondaryOverlayForRHPOnSuperWideRHP} = useContext(WideRHPContext); + const {shouldRenderSecondaryOverlayForWideRHP, shouldRenderSecondaryOverlayForRHPOnWideRHP, shouldRenderSecondaryOverlayForRHPOnSuperWideRHP} = useWideRHPState(); const {sidePanelOffset} = useSidePanel(); if (shouldRenderSecondaryOverlayForWideRHP) { @@ -91,7 +92,8 @@ function RightModalNavigator({navigation, route}: RightModalNavigatorProps) { const {isSmallScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); const isExecutingRef = useRef(false); const screenOptions = useRHPScreenOptions(); - const {superWideRHPRouteKeys, shouldRenderTertiaryOverlay, clearWideRHPKeys, syncRHPKeys} = useContext(WideRHPContext); + const {superWideRHPRouteKeys, shouldRenderTertiaryOverlay} = useWideRHPState(); + const {clearWideRHPKeys, syncRHPKeys} = useWideRHPActions(); const {windowWidth} = useWindowDimensions(); const modalStackScreenOptions = useModalStackScreenOptions(); const styles = useThemeStyles(); diff --git a/src/libs/Navigation/AppNavigator/useRHPScreenOptions.ts b/src/libs/Navigation/AppNavigator/useRHPScreenOptions.ts index 2d5ec1d881c6..be473995f397 100644 --- a/src/libs/Navigation/AppNavigator/useRHPScreenOptions.ts +++ b/src/libs/Navigation/AppNavigator/useRHPScreenOptions.ts @@ -1,7 +1,7 @@ import {CardStyleInterpolators} from '@react-navigation/stack'; import type {StackCardInterpolationProps} from '@react-navigation/stack'; -import {useContext, useMemo} from 'react'; -import {WideRHPContext} from '@components/WideRHPContextProvider'; +import {useMemo} from 'react'; +import {useWideRHPState} from '@components/WideRHPContextProvider'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import {isSafari} from '@libs/Browser'; @@ -28,7 +28,7 @@ const getModifiedCardStyleInterpolatorProps = (props: StackCardInterpolationProp const useRHPScreenOptions = (): PlatformStackNavigationOptions => { const styles = useThemeStyles(); const customInterpolator = useModalCardStyleInterpolator(); - const {wideRHPRouteKeys} = useContext(WideRHPContext); + const {wideRHPRouteKeys} = useWideRHPState(); // We have to use the isSmallScreenWidth instead of shouldUseNarrow layout, because we want to have information about screen width without the context of side modal. // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth diff --git a/src/pages/TransactionDuplicate/Confirmation.tsx b/src/pages/TransactionDuplicate/Confirmation.tsx index eba4c10cdc0d..1e810659592c 100644 --- a/src/pages/TransactionDuplicate/Confirmation.tsx +++ b/src/pages/TransactionDuplicate/Confirmation.tsx @@ -1,5 +1,5 @@ import {useRoute} from '@react-navigation/native'; -import React, {useCallback, useContext, useMemo} from 'react'; +import React, {useCallback, useMemo} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -12,7 +12,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import {ShowContextMenuContext} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; -import {WideRHPContext} from '@components/WideRHPContextProvider'; +import {useWideRHPState} from '@components/WideRHPContextProvider'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -65,7 +65,7 @@ function Confirmation() { const reportAction = Object.values(reportActions ?? {}).find( (action) => ReportActionsUtils.isMoneyRequestAction(action) && ReportActionsUtils.getOriginalMessage(action)?.IOUTransactionID === reviewDuplicates?.transactionID, ); - const {superWideRHPRouteKeys} = useContext(WideRHPContext); + const {superWideRHPRouteKeys} = useWideRHPState(); const isSuperWideRHPDisplayed = superWideRHPRouteKeys.length > 0; const [duplicates] = useTransactionsByID(reviewDuplicates?.duplicates); diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 632cec2cf829..8bf1c25bf932 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -11,7 +11,7 @@ import type {Emoji} from '@assets/emojis/types'; import type {MeasureParentContainerAndCursorCallback} from '@components/AutoCompleteSuggestions/types'; import Composer from '@components/Composer'; import type {CustomSelectionChangeEvent, TextSelection} from '@components/Composer/types'; -import {WideRHPContext} from '@components/WideRHPContextProvider'; +import {useWideRHPState} from '@components/WideRHPContextProvider'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useKeyboardState from '@hooks/useKeyboardState'; import useLocalize from '@hooks/useLocalize'; @@ -258,7 +258,7 @@ function ComposerWithSuggestions({ const commentRef = useRef(value); - const {superWideRHPRouteKeys} = useContext(WideRHPContext); + const {superWideRHPRouteKeys} = useWideRHPState(); // Autofocus is disabled on SearchReport when another RHP is displayed below as it causes animation issues const shouldDisableAutoFocus = superWideRHPRouteKeys.length > 0 && route.name === SCREENS.RIGHT_MODAL.SEARCH_REPORT; diff --git a/src/pages/iou/RejectReasonPage.tsx b/src/pages/iou/RejectReasonPage.tsx index f70f5beacab1..69634ad9f227 100644 --- a/src/pages/iou/RejectReasonPage.tsx +++ b/src/pages/iou/RejectReasonPage.tsx @@ -3,7 +3,7 @@ import React, {useCallback, useContext, useEffect} from 'react'; import {DelegateNoAccessContext} from '@components/DelegateNoAccessModalProvider'; import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; import {useSearchContext} from '@components/Search/SearchContext'; -import {WideRHPContext} from '@components/WideRHPContextProvider'; +import {useWideRHPState} from '@components/WideRHPContextProvider'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; @@ -31,7 +31,7 @@ function RejectReasonPage({route}: RejectReasonPageProps) { const {removeTransaction} = useSearchContext(); const [reportPolicyID] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(reportID)}`, {canBeMissing: false, selector: getReportPolicyID}); const policy = usePolicy(reportPolicyID); - const {superWideRHPRouteKeys} = useContext(WideRHPContext); + const {superWideRHPRouteKeys} = useWideRHPState(); const {isDelegateAccessRestricted, showDelegateNoAccessModal} = useContext(DelegateNoAccessContext); const onSubmit = (values: FormOnyxValues) => { From 00090c2549336a5bad299de9d49c220a500a477e Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 28 Jan 2026 11:42:25 +0100 Subject: [PATCH 2/5] Remove unnecessary useContext import after merge --- .../MoneyRequestReportTransactionList.tsx | 2 +- src/components/ReportActionItem/MoneyRequestView.tsx | 2 +- src/components/Search/index.tsx | 2 +- src/libs/Navigation/AppNavigator/AuthScreens.tsx | 2 +- .../ComposerWithSuggestions/ComposerWithSuggestions.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx index 1a3dccffa81d..7f6c2bd21a48 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx @@ -1,6 +1,6 @@ import {useFocusEffect} from '@react-navigation/native'; import isEmpty from 'lodash/isEmpty'; -import React, {memo, useCallback, useContext, useEffect, useMemo, useState} from 'react'; +import React, {memo, useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import type {TupleToUnion} from 'type-fest'; import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu'; diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 69cda2e8e7e9..9d8489ed232b 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -1,5 +1,5 @@ import {Str} from 'expensify-common'; -import React, {useCallback, useContext, useEffect, useMemo, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import DotIndicatorMessage from '@components/DotIndicatorMessage'; diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index ebc71a39cd63..21c1dcebc3b2 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1,6 +1,6 @@ import {findFocusedRoute, useFocusEffect, useIsFocused, useNavigation} from '@react-navigation/native'; import * as Sentry from '@sentry/react-native'; -import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import type {NativeScrollEvent, NativeSyntheticEvent, StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 14359a6ee094..a89c797d0420 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -1,7 +1,7 @@ import type {RouteProp} from '@react-navigation/native'; import {useNavigationState} from '@react-navigation/native'; import type {StackCardInterpolationProps} from '@react-navigation/stack'; -import React, {memo, useContext, useEffect, useRef, useState} from 'react'; +import React, {memo, useEffect, useRef, useState} from 'react'; import ComposeProviders from '@components/ComposeProviders'; import OpenConfirmNavigateExpensifyClassicModal from '@components/ConfirmNavigateExpensifyClassicModal'; import {CurrencyListContextProvider} from '@components/CurrencyListContextProvider'; diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 287af0fd80fc..9c17ef812bda 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -1,7 +1,7 @@ import {useIsFocused, useNavigation, useRoute} from '@react-navigation/native'; import lodashDebounce from 'lodash/debounce'; import type {ForwardedRef, RefObject} from 'react'; -import React, {memo, useCallback, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; +import React, {memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import type {BlurEvent, LayoutChangeEvent, MeasureInWindowOnSuccessCallback, TextInput, TextInputContentSizeChangeEvent, TextInputKeyPressEvent, TextInputScrollEvent} from 'react-native'; import {DeviceEventEmitter, InteractionManager, NativeModules, StyleSheet, View} from 'react-native'; import {useFocusedInputHandler} from 'react-native-keyboard-controller'; From c62a6b721bb5daa591e1c71599826d0322b7b4fc Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 28 Jan 2026 11:52:32 +0100 Subject: [PATCH 3/5] Remove manual memoization in WideRHPContextProvider --- .../WideRHPContextProvider/index.tsx | 200 +++++++----------- 1 file changed, 78 insertions(+), 122 deletions(-) diff --git a/src/components/WideRHPContextProvider/index.tsx b/src/components/WideRHPContextProvider/index.tsx index 438d5e31c950..fef9b953449f 100644 --- a/src/components/WideRHPContextProvider/index.tsx +++ b/src/components/WideRHPContextProvider/index.tsx @@ -1,5 +1,5 @@ import {findFocusedRoute} from '@react-navigation/native'; -import React, {createContext, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; +import React, {createContext, useContext, useEffect, useRef, useState} from 'react'; // We use Animated for all functionality related to wide RHP to make it easier // to interact with react-navigation components (e.g., CardContainer, interpolator), which also use Animated. // eslint-disable-next-line no-restricted-imports @@ -115,13 +115,13 @@ function WideRHPContextProvider({children}: React.PropsWithChildren) { const isWideRHPClosingRef = useRef(false); const isSuperWideRHPClosingRef = useRef(false); - const setIsWideRHPClosing = useCallback((isClosing: boolean) => { + const setIsWideRHPClosing = (isClosing: boolean) => { isWideRHPClosingRef.current = isClosing; - }, []); + }; - const setIsSuperWideRHPClosing = useCallback((isClosing: boolean) => { + const setIsSuperWideRHPClosing = (isClosing: boolean) => { isSuperWideRHPClosingRef.current = isClosing; - }, []); + }; const {focusedRoute, focusedNavigator} = useRootNavigationState((state) => { if (!state) { @@ -134,13 +134,8 @@ function WideRHPContextProvider({children}: React.PropsWithChildren) { }; }); - const isWideRHPFocused = (() => { - return !!focusedRoute?.key && allWideRHPRouteKeys.includes(focusedRoute.key); - })(); - - const isSuperWideRHPFocused = (() => { - return !!focusedRoute?.key && allSuperWideRHPRouteKeys.includes(focusedRoute.key); - })(); + const isWideRHPFocused = !!focusedRoute?.key && allWideRHPRouteKeys.includes(focusedRoute.key); + const isSuperWideRHPFocused = !!focusedRoute?.key && allSuperWideRHPRouteKeys.includes(focusedRoute.key); const isRHPFocused = focusedNavigator === NAVIGATORS.RIGHT_MODAL_NAVIGATOR; @@ -148,21 +143,22 @@ function WideRHPContextProvider({children}: React.PropsWithChildren) { const {isWideRHPBelow, isSuperWideRHPBelow} = getIsRHPDisplayedBelow(focusedRoute?.key, allSuperWideRHPRouteKeys, allWideRHPRouteKeys); // Updates the Wide RHP visible keys table from the all keys table - const syncRHPKeys = useCallback(() => { + const syncRHPKeys = () => { const {visibleSuperWideRHPRouteKeys, visibleWideRHPRouteKeys} = getVisibleRHPKeys(allSuperWideRHPRouteKeys, allWideRHPRouteKeys); setWideRHPRouteKeys(visibleWideRHPRouteKeys); setSuperWideRHPRouteKeys(visibleSuperWideRHPRouteKeys); setExpandedRHPProgress(visibleSuperWideRHPRouteKeys, visibleWideRHPRouteKeys); - }, [allSuperWideRHPRouteKeys, allWideRHPRouteKeys]); + }; - const clearWideRHPKeys = useCallback(() => { + const clearWideRHPKeys = () => { setWideRHPRouteKeys([]); setSuperWideRHPRouteKeys([]); expandedRHPProgress.setValue(0); - }, []); + }; // Once we have updated the array of all Super Wide RHP keys, we should sync it with the array of RHP keys visible on the screen useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect syncRHPKeys(); }, [allSuperWideRHPRouteKeys, allWideRHPRouteKeys, syncRHPKeys]); @@ -192,109 +188,94 @@ function WideRHPContextProvider({children}: React.PropsWithChildren) { /** * Removes a route from the super wide RHP route keys list, disabling wide RHP display for that route. */ - const removeSuperWideRHPRouteKey = useCallback((route: NavigationRoute) => removeWideRHPRoute(route, setAllSuperWideRHPRouteKeys), []); + const removeSuperWideRHPRouteKey = (route: NavigationRoute) => removeWideRHPRoute(route, setAllSuperWideRHPRouteKeys); /** * Removes a route from the wide RHP route keys list, disabling wide RHP display for that route. */ - const removeWideRHPRouteKey = useCallback((route: NavigationRoute) => removeWideRHPRoute(route, setAllWideRHPRouteKeys), []); + const removeWideRHPRouteKey = (route: NavigationRoute) => removeWideRHPRoute(route, setAllWideRHPRouteKeys); /** * Adds a route to the wide RHP route keys list, enabling wide RHP display for that route. */ - const showWideRHPVersion = useCallback( - (route: NavigationRoute) => { - removeSuperWideRHPRouteKey(route); - showWideRHPRoute(route, setAllWideRHPRouteKeys); - }, - [removeSuperWideRHPRouteKey], - ); + const showWideRHPVersion = (route: NavigationRoute) => { + removeSuperWideRHPRouteKey(route); + showWideRHPRoute(route, setAllWideRHPRouteKeys); + }; /** * Adds a route to the super wide RHP route keys list, enabling super wide RHP display for that route. */ - const showSuperWideRHPVersion = useCallback( - (route: NavigationRoute) => { - removeWideRHPRouteKey(route); - showWideRHPRoute(route, setAllSuperWideRHPRouteKeys); - }, - [removeWideRHPRouteKey], - ); + const showSuperWideRHPVersion = (route: NavigationRoute) => { + removeWideRHPRouteKey(route); + showWideRHPRoute(route, setAllSuperWideRHPRouteKeys); + }; /** * Marks a report ID as an expense report, adding it to the expense reports set. * This enables optimistic wide RHP display for expense reports. * It helps us open expense as wide, before it fully loads. */ - const markReportIDAsExpense = useCallback( - (reportID?: string) => { - if (!reportID) { - return; - } - const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; - const isInvoice = report?.type === CONST.REPORT.TYPE.INVOICE; - const isTask = report?.type === CONST.REPORT.TYPE.TASK; - if (isInvoice || isTask) { - return; - } - setExpenseReportIDs((prev) => { - const newSet = new Set(prev); - newSet.add(reportID); - return newSet; - }); - }, - [allReports], - ); + const markReportIDAsExpense = (reportID?: string) => { + if (!reportID) { + return; + } + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const isInvoice = report?.type === CONST.REPORT.TYPE.INVOICE; + const isTask = report?.type === CONST.REPORT.TYPE.TASK; + if (isInvoice || isTask) { + return; + } + setExpenseReportIDs((prev) => { + const newSet = new Set(prev); + newSet.add(reportID); + return newSet; + }); + }; /** * Checks if a report ID is marked as an expense report. * Used to determine if wide RHP should be displayed optimistically. * It helps us open expense as wide, before it fully loads. */ - const isReportIDMarkedAsExpense = useCallback( - (reportID: string) => { - return expenseReportIDs.has(reportID); - }, - [expenseReportIDs], - ); + const isReportIDMarkedAsExpense = (reportID: string) => { + return expenseReportIDs.has(reportID); + }; /** * Marks a report ID as a multi-transaction expense report, adding it to the expense reports set. * This enables optimistic super wide RHP display for expense reports. * It helps us open expense as super wide, before it fully loads. */ - const markReportIDAsMultiTransactionExpense = useCallback((reportID: string) => { + const markReportIDAsMultiTransactionExpense = (reportID: string) => { setMultiTransactionExpenseReportIDs((prev) => { const newSet = new Set(prev); newSet.add(reportID); return newSet; }); - }, []); + }; /** * Removes a report ID from the multi-transaction expense reports set. * This disables optimistic super wide RHP display for that specific report * (e.g., when transactions are deleted or report no longer qualifies as multi-transaction) */ - const unmarkReportIDAsMultiTransactionExpense = useCallback((reportID: string) => { + const unmarkReportIDAsMultiTransactionExpense = (reportID: string) => { setMultiTransactionExpenseReportIDs((prev) => { const newSet = new Set(prev); newSet.delete(reportID); return newSet; }); - }, []); + }; /** * Checks if a report ID is marked as a multi-transaction expense report. * Used to determine if super wide RHP should be displayed optimistically. * It helps us open expense as super wide, before it fully loads. */ - const isReportIDMarkedAsMultiTransactionExpense = useCallback( - (reportID: string) => { - return multiTransactionExpenseReportIDs.has(reportID); - }, - [multiTransactionExpenseReportIDs], - ); + const isReportIDMarkedAsMultiTransactionExpense = (reportID: string) => { + return multiTransactionExpenseReportIDs.has(reportID); + }; /** * Effect that handles responsive RHP width calculation when window dimensions change. @@ -323,61 +304,36 @@ function WideRHPContextProvider({children}: React.PropsWithChildren) { return () => subscription?.remove(); }, []); - const stateValue = useMemo( - () => ({ - wideRHPRouteKeys, - superWideRHPRouteKeys, - shouldRenderSecondaryOverlayForRHPOnSuperWideRHP, - shouldRenderSecondaryOverlayForRHPOnWideRHP, - shouldRenderSecondaryOverlayForWideRHP, - shouldRenderTertiaryOverlay, - isWideRHPFocused, - isSuperWideRHPFocused, - }), - [ - wideRHPRouteKeys, - superWideRHPRouteKeys, - shouldRenderSecondaryOverlayForRHPOnSuperWideRHP, - shouldRenderSecondaryOverlayForRHPOnWideRHP, - shouldRenderSecondaryOverlayForWideRHP, - shouldRenderTertiaryOverlay, - isWideRHPFocused, - isSuperWideRHPFocused, - ], - ); - - const actionsValue = useMemo( - () => ({ - showWideRHPVersion, - showSuperWideRHPVersion, - removeWideRHPRouteKey, - removeSuperWideRHPRouteKey, - markReportIDAsExpense, - markReportIDAsMultiTransactionExpense, - unmarkReportIDAsMultiTransactionExpense, - isReportIDMarkedAsExpense, - isReportIDMarkedAsMultiTransactionExpense, - syncRHPKeys, - clearWideRHPKeys, - setIsWideRHPClosing, - setIsSuperWideRHPClosing, - }), - [ - showWideRHPVersion, - showSuperWideRHPVersion, - removeWideRHPRouteKey, - removeSuperWideRHPRouteKey, - markReportIDAsExpense, - markReportIDAsMultiTransactionExpense, - unmarkReportIDAsMultiTransactionExpense, - isReportIDMarkedAsExpense, - isReportIDMarkedAsMultiTransactionExpense, - syncRHPKeys, - clearWideRHPKeys, - setIsWideRHPClosing, - setIsSuperWideRHPClosing, - ], - ); + // Because of the React Compiler we don't need to memoize it manually + // eslint-disable-next-line react/jsx-no-constructed-context-values + const stateValue = { + wideRHPRouteKeys, + superWideRHPRouteKeys, + shouldRenderSecondaryOverlayForRHPOnSuperWideRHP, + shouldRenderSecondaryOverlayForRHPOnWideRHP, + shouldRenderSecondaryOverlayForWideRHP, + shouldRenderTertiaryOverlay, + isWideRHPFocused, + isSuperWideRHPFocused, + }; + + // Because of the React Compiler we don't need to memoize it manually + // eslint-disable-next-line react/jsx-no-constructed-context-values + const actionsValue = { + showWideRHPVersion, + showSuperWideRHPVersion, + removeWideRHPRouteKey, + removeSuperWideRHPRouteKey, + markReportIDAsExpense, + markReportIDAsMultiTransactionExpense, + unmarkReportIDAsMultiTransactionExpense, + isReportIDMarkedAsExpense, + isReportIDMarkedAsMultiTransactionExpense, + syncRHPKeys, + clearWideRHPKeys, + setIsWideRHPClosing, + setIsSuperWideRHPClosing, + }; return ( From 03715ac4eab44fdfe96f76dcebadf98017ac6e7f Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 28 Jan 2026 12:10:52 +0100 Subject: [PATCH 4/5] Remove useContext import from BaseValidateCodeForm --- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx index b7975b9b9e8a..ba10bf5ee4c6 100644 --- a/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -1,6 +1,6 @@ import {useFocusEffect} from '@react-navigation/native'; import type {ForwardedRef} from 'react'; -import React, {useCallback, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import type {StyleProp, ViewStyle} from 'react-native'; import Button from '@components/Button'; From 611485969558c7f4c33f56499d38646495c35f1c Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 28 Jan 2026 16:41:51 +0100 Subject: [PATCH 5/5] Readd manual useCallback to WideRHPContext to get rid of a warning --- src/components/WideRHPContextProvider/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/WideRHPContextProvider/index.tsx b/src/components/WideRHPContextProvider/index.tsx index fef9b953449f..5af9a4129411 100644 --- a/src/components/WideRHPContextProvider/index.tsx +++ b/src/components/WideRHPContextProvider/index.tsx @@ -1,5 +1,5 @@ import {findFocusedRoute} from '@react-navigation/native'; -import React, {createContext, useContext, useEffect, useRef, useState} from 'react'; +import React, {createContext, useCallback, useContext, useEffect, useRef, useState} from 'react'; // We use Animated for all functionality related to wide RHP to make it easier // to interact with react-navigation components (e.g., CardContainer, interpolator), which also use Animated. // eslint-disable-next-line no-restricted-imports @@ -143,12 +143,12 @@ function WideRHPContextProvider({children}: React.PropsWithChildren) { const {isWideRHPBelow, isSuperWideRHPBelow} = getIsRHPDisplayedBelow(focusedRoute?.key, allSuperWideRHPRouteKeys, allWideRHPRouteKeys); // Updates the Wide RHP visible keys table from the all keys table - const syncRHPKeys = () => { + const syncRHPKeys = useCallback(() => { const {visibleSuperWideRHPRouteKeys, visibleWideRHPRouteKeys} = getVisibleRHPKeys(allSuperWideRHPRouteKeys, allWideRHPRouteKeys); setWideRHPRouteKeys(visibleWideRHPRouteKeys); setSuperWideRHPRouteKeys(visibleSuperWideRHPRouteKeys); setExpandedRHPProgress(visibleSuperWideRHPRouteKeys, visibleWideRHPRouteKeys); - }; + }, [allSuperWideRHPRouteKeys, allWideRHPRouteKeys]); const clearWideRHPKeys = () => { setWideRHPRouteKeys([]);