From a2e5bfc082042e2b05cbcd2f2b567c574201d2c3 Mon Sep 17 00:00:00 2001 From: Test Date: Sun, 1 Mar 2026 16:01:06 +0100 Subject: [PATCH] perf: optimize ManualOpenCreateExpense metric with policy selector and reduced subscriptions --- src/libs/actions/IOU/index.ts | 2 +- src/pages/iou/request/IOURequestStartPage.tsx | 63 +++++++++++++++---- .../step/IOURequestStepConfirmation.tsx | 5 +- 3 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index a0bfbacf8126b..39d6966c51e57 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -1219,7 +1219,7 @@ function initMoneyRequest({ newIouRequestType, report, parentReport, - currentDate = '', + currentDate, lastSelectedDistanceRates, currentUserPersonalDetails, hasOnlyPersonalPolicies, diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index b642e8d2bd0e4..380b1aad50343 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -1,6 +1,7 @@ import {useFocusEffect} from '@react-navigation/native'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {Keyboard, View} from 'react-native'; +import type {OnyxCollection} from 'react-native-onyx'; import DragAndDropProvider from '@components/DragAndDrop/Provider'; import FocusTrapContainerElement from '@components/FocusTrap/FocusTrapContainerElement'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -38,10 +39,11 @@ import {endSpan} from '@libs/telemetry/activeSpans'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {IOURequestType} from '@userActions/IOU'; import {initMoneyRequest} from '@userActions/IOU'; +import Tab from '@userActions/Tab'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import type {SelectedTabRequest} from '@src/types/onyx'; +import type {Policy, SelectedTabRequest} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import IOURequestStepAmount from './step/IOURequestStepAmount'; @@ -60,6 +62,45 @@ type IOURequestStartPageProps = WithWritableReportOrNotFoundProps): OnyxCollection => { + if (!policies) { + return {}; + } + + const result: Record = {}; + + for (const [id, policyItem] of Object.entries(policies)) { + if (!policyItem) { + continue; + } + + result[id] = { + id: policyItem.id, + type: policyItem.type, + name: policyItem.name, + pendingAction: policyItem.pendingAction, + isPolicyExpenseChatEnabled: policyItem.isPolicyExpenseChatEnabled, + role: policyItem.role, + chatReportIDAdmins: policyItem.chatReportIDAdmins, + employeeList: policyItem.employeeList, + arePerDiemRatesEnabled: policyItem.arePerDiemRatesEnabled, + customUnits: policyItem.customUnits, + units: policyItem.units, + // Additional fields required by policy utilities + isJoinRequestPending: policyItem.isJoinRequestPending, + errors: policyItem.errors, + owner: policyItem.owner, + areInvoicesEnabled: policyItem.areInvoicesEnabled, + } as Policy; + } + + return result; +}; + function IOURequestStartPage({ route, route: { @@ -77,12 +118,16 @@ function IOURequestStartPage({ const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`); const policy = usePolicy(report?.policyID); const [lastSelectedTab, selectedTabResult] = useOnyx(`${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.IOU_REQUEST_TYPE}`); - const [selectedTab, setSelectedTab] = useState(lastSelectedTab); + // Derive selectedTab directly instead of using state + const selectedTab = lastSelectedTab; const isLoadingSelectedTab = shouldUseTab ? isLoadingOnyxValue(selectedTabResult) : false; const [transaction, transactionResult] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${getNonEmptyStringOnyxID(route?.params.transactionID)}`); const isLoadingTransaction = isLoadingOnyxValue(transactionResult); - const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); + const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, { + selector: policySelector, + }); + const [lastSelectedDistanceRates] = useOnyx(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES); const [draftTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT); const [isMultiScanEnabled, setIsMultiScanEnabled] = useState(false); @@ -109,8 +154,7 @@ function IOURequestStartPage({ // We requestAnimationFrame since the function is called in the animate block in the web implementation // which fixes a locked animation glitch when swiping between tabs, and aligns with the native implementation internal delay requestAnimationFrame(() => { - // 2 - PerDiem - if (index !== 2) { + if (index !== PER_DIEM_TAB_INDEX) { return; } perDiemInputRef.current?.focus?.(); @@ -164,12 +208,7 @@ function IOURequestStartPage({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - useEffect(() => { - if (isLoadingSelectedTab || selectedTab) { - return; - } - setSelectedTab(lastSelectedTab); - }, [isLoadingSelectedTab, selectedTab, lastSelectedTab]); + // Removed useEffect that was syncing selectedTab state - now derived directly const navigateBack = () => { Navigation.closeRHPFlow(); @@ -219,7 +258,7 @@ function IOURequestStartPage({ const onTabSelected = useCallback( (newIouType: IOURequestType) => { - setSelectedTab(newIouType); + Tab.setSelectedTab(CONST.TAB.IOU_REQUEST_TYPE, newIouType); resetIOUTypeIfChanged(newIouType); }, [resetIOUTypeIfChanged], diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index e18cb7f50242a..640a9aa5c9986 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -159,7 +159,6 @@ function IOURequestStepConfirmation({ const draftPolicyID = getIOURequestPolicyID(initialTransaction, reportDraft); const [policyDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${draftPolicyID}`); const [policyReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${realPolicyID}`); - const [reportDrafts] = useOnyx(ONYXKEYS.COLLECTION.REPORT_DRAFT); const [gpsDraftDetails] = useOnyx(ONYXKEYS.GPS_DRAFT_DETAILS); const [betas] = useOnyx(ONYXKEYS.BETAS); @@ -310,9 +309,9 @@ function IOURequestStepConfirmation({ const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${participant.reportID}`]; return participant.accountID ? getParticipantsOption(participant, personalDetails) - : getReportOption(participant, privateIsArchived, policy, currentUserPersonalDetails.accountID, personalDetails, reportAttributesDerived, reportDrafts); + : getReportOption(participant, privateIsArchived, policy, currentUserPersonalDetails.accountID, personalDetails, reportAttributesDerived); }) ?? [], - [transaction?.participants, iouType, personalDetails, reportAttributesDerived, reportDrafts, privateIsArchivedMap, policy, currentUserPersonalDetails.accountID], + [transaction?.participants, iouType, personalDetails, reportAttributesDerived, privateIsArchivedMap, policy, currentUserPersonalDetails.accountID], ); const isPolicyExpenseChat = useMemo(() => participants?.some((participant) => participant.isPolicyExpenseChat), [participants]); const shouldGenerateTransactionThreadReport = !isBetaEnabled(CONST.BETAS.NO_OPTIMISTIC_TRANSACTION_THREADS);