Skip to content

Commit 5a2d4dd

Browse files
authored
Merge pull request Expensify#87959 from callstack-internal/perf/extract-useResetIOUType-shared-hook
extract useResetIOUType hook to deduplicate tab logic
2 parents affa37f + 05b8a2c commit 5a2d4dd

4 files changed

Lines changed: 167 additions & 173 deletions

File tree

src/hooks/useResetIOUType.ts

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import {useFocusEffect} from '@react-navigation/native';
2+
import {hasOnlyPersonalPoliciesSelector} from '@selectors/Policy';
3+
import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft';
4+
import {useRef} from 'react';
5+
import {Keyboard} from 'react-native';
6+
import type {OnyxEntry} from 'react-native-onyx';
7+
import type {IOURequestType} from '@userActions/IOU';
8+
import {initMoneyRequest} from '@userActions/IOU';
9+
import CONST from '@src/CONST';
10+
import ONYXKEYS from '@src/ONYXKEYS';
11+
import type {Policy, Report, Transaction} from '@src/types/onyx';
12+
import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails';
13+
import useOnyx from './useOnyx';
14+
import usePersonalPolicy from './usePersonalPolicy';
15+
import usePrevious from './usePrevious';
16+
17+
type UseResetIOUTypeParams = {
18+
/** The report ID from the route params */
19+
reportID: string;
20+
21+
/** The current report object */
22+
report: OnyxEntry<Report>;
23+
24+
/** The current draft transaction */
25+
transaction: OnyxEntry<Transaction>;
26+
27+
/** Whether the transaction data is still loading */
28+
isLoadingTransaction?: boolean;
29+
30+
/** Whether the selected tab data is still loading */
31+
isLoadingSelectedTab?: boolean;
32+
33+
/** The current transaction request type derived from tab/transaction state */
34+
transactionRequestType: IOURequestType | undefined;
35+
36+
/** The policy resolved for this transaction */
37+
policy?: OnyxEntry<Policy>;
38+
39+
/** Whether this is a track distance expense */
40+
isTrackDistanceExpense?: boolean;
41+
42+
/** Whether to skip keyboard dismiss for per diem tab */
43+
skipKeyboardDismissForPerDiem?: boolean;
44+
};
45+
46+
/**
47+
* Shared hook that encapsulates the tab-reset logic duplicated between
48+
* `IOURequestStartPage` and `DistanceRequestStartPage`.
49+
*/
50+
function useResetIOUType({
51+
reportID,
52+
report,
53+
transaction,
54+
isLoadingTransaction = false,
55+
isLoadingSelectedTab = false,
56+
transactionRequestType,
57+
policy,
58+
isTrackDistanceExpense = false,
59+
skipKeyboardDismissForPerDiem = false,
60+
}: UseResetIOUTypeParams): (newIOUType: IOURequestType) => void {
61+
const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`);
62+
const [hasOnlyPersonalPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: hasOnlyPersonalPoliciesSelector});
63+
const [lastSelectedDistanceRates] = useOnyx(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES);
64+
const [currentDate] = useOnyx(ONYXKEYS.CURRENT_DATE);
65+
const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector});
66+
67+
const personalPolicy = usePersonalPolicy();
68+
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
69+
70+
const resetIOUTypeIfChanged = (newIOUType: IOURequestType) => {
71+
if (!(skipKeyboardDismissForPerDiem && newIOUType === CONST.IOU.REQUEST_TYPE.PER_DIEM)) {
72+
Keyboard.dismiss();
73+
}
74+
75+
if (transaction?.iouRequestType === newIOUType) {
76+
return;
77+
}
78+
79+
const isFromGlobalCreate = !report?.reportID;
80+
81+
initMoneyRequest({
82+
reportID,
83+
policy,
84+
personalPolicy,
85+
isFromGlobalCreate,
86+
isTrackDistanceExpense,
87+
isFromFloatingActionButton: transaction?.isFromFloatingActionButton ?? transaction?.isFromGlobalCreate ?? isFromGlobalCreate,
88+
currentIouRequestType: transaction?.iouRequestType,
89+
newIouRequestType: newIOUType,
90+
report,
91+
parentReport,
92+
currentDate,
93+
lastSelectedDistanceRates,
94+
currentUserPersonalDetails,
95+
hasOnlyPersonalPolicies: hasOnlyPersonalPolicies ?? true,
96+
draftTransactionIDs,
97+
});
98+
};
99+
100+
const tabSelectedTypeRef = useRef<IOURequestType | null>(null);
101+
102+
const onTabSelected = (newIouType: IOURequestType) => {
103+
tabSelectedTypeRef.current = newIouType;
104+
resetIOUTypeIfChanged(newIouType);
105+
};
106+
107+
const prevTransactionReportID = usePrevious(transaction?.reportID);
108+
const personalPolicyID = personalPolicy?.id;
109+
110+
// Clear out the temporary expense if the reportID in the URL has changed from the transaction's reportID.
111+
useFocusEffect(() => {
112+
// Skip until transactionRequestType catches up with the tab onTabSelected already set.
113+
if (tabSelectedTypeRef.current && transactionRequestType !== tabSelectedTypeRef.current) {
114+
return;
115+
}
116+
tabSelectedTypeRef.current = null;
117+
118+
// The test transaction can change the reportID of the transaction on the flow so we should prevent the reportID from being reverted again.
119+
if (
120+
transaction?.reportID === reportID ||
121+
isLoadingTransaction ||
122+
isLoadingSelectedTab ||
123+
!transactionRequestType ||
124+
prevTransactionReportID !== transaction?.reportID ||
125+
!personalPolicyID
126+
) {
127+
return;
128+
}
129+
resetIOUTypeIfChanged(transactionRequestType);
130+
});
131+
132+
return onTabSelected;
133+
}
134+
135+
export default useResetIOUType;

src/pages/iou/request/DistanceRequestStartPage.tsx

Lines changed: 12 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,25 @@
1-
import {useFocusEffect} from '@react-navigation/native';
2-
import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft';
3-
import React, {useCallback, useEffect, useMemo, useState} from 'react';
4-
import {Keyboard, View} from 'react-native';
1+
import React, {useEffect, useMemo, useState} from 'react';
2+
import {View} from 'react-native';
53
import FocusTrapContainerElement from '@components/FocusTrap/FocusTrapContainerElement';
64
import HeaderWithBackButton from '@components/HeaderWithBackButton';
75
import ScreenWrapper from '@components/ScreenWrapper';
86
import TabSelector from '@components/TabSelector/TabSelector';
9-
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
107
import useLocalize from '@hooks/useLocalize';
118
import useOnyx from '@hooks/useOnyx';
12-
import usePersonalPolicy from '@hooks/usePersonalPolicy';
139
import usePolicyForTransaction from '@hooks/usePolicyForTransaction';
14-
import usePrevious from '@hooks/usePrevious';
10+
import useResetIOUType from '@hooks/useResetIOUType';
1511
import useThemeStyles from '@hooks/useThemeStyles';
1612
import {canUseTouchScreen} from '@libs/DeviceCapabilities';
1713
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
1814
import Navigation from '@libs/Navigation/Navigation';
1915
import OnyxTabNavigator, {TabScreenWithFocusTrapWrapper, TopTab} from '@libs/Navigation/OnyxTabNavigator';
20-
import {hasOnlyPersonalPolicies as hasOnlyPersonalPoliciesUtil} from '@libs/PolicyUtils';
2116
import {getPayeeName} from '@libs/ReportUtils';
2217
import {endSpan} from '@libs/telemetry/activeSpans';
2318
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
24-
import type {IOURequestType} from '@userActions/IOU';
25-
import {initMoneyRequest} from '@userActions/IOU';
2619
import CONST from '@src/CONST';
2720
import ONYXKEYS from '@src/ONYXKEYS';
2821
import type SCREENS from '@src/SCREENS';
2922
import type {SelectedTabRequest} from '@src/types/onyx';
30-
import {isEmptyObject} from '@src/types/utils/EmptyObject';
3123
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
3224
import IOURequestStepDistanceGPS from './step/IOURequestStepDistanceGPS';
3325
import IOURequestStepDistanceManual from './step/IOURequestStepDistanceManual';
@@ -51,22 +43,13 @@ function DistanceRequestStartPage({
5143
const styles = useThemeStyles();
5244
const {translate} = useLocalize();
5345
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
54-
const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`);
5546
const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${getNonEmptyStringOnyxID(route?.params.transactionID)}`);
5647
const {policy} = usePolicyForTransaction({transaction, reportPolicyID: report?.policyID, action, iouType});
5748
const [selectedTab, selectedTabResult] = useOnyx(`${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.DISTANCE_REQUEST_TYPE}`);
5849
const [lastDistanceExpenseType] = useOnyx(ONYXKEYS.NVP_LAST_DISTANCE_EXPENSE_TYPE);
5950
const isLoadingSelectedTab = isLoadingOnyxValue(selectedTabResult);
60-
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
61-
const [lastSelectedDistanceRates] = useOnyx(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES);
62-
const [currentDate] = useOnyx(ONYXKEYS.CURRENT_DATE);
6351
const isTrackDistanceExpense = iouType === CONST.IOU.TYPE.TRACK;
6452

65-
const hasOnlyPersonalPolicies = useMemo(() => hasOnlyPersonalPoliciesUtil(allPolicies), [allPolicies]);
66-
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
67-
const personalPolicy = usePersonalPolicy();
68-
const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector});
69-
7053
const tabTitles = {
7154
[CONST.IOU.TYPE.REQUEST]: translate('iou.trackDistance'),
7255
[CONST.IOU.TYPE.SUBMIT]: translate('iou.trackDistance'),
@@ -79,8 +62,6 @@ function DistanceRequestStartPage({
7962
[CONST.IOU.TYPE.CREATE]: translate('iou.trackDistance'),
8063
};
8164

82-
const isFromGlobalCreate = isEmptyObject(report?.reportID);
83-
8465
const transactionRequestType = useMemo(() => {
8566
if (!transaction?.iouRequestType) {
8667
return lastDistanceExpenseType ?? selectedTab ?? CONST.IOU.REQUEST_TYPE.DISTANCE_MAP;
@@ -89,7 +70,15 @@ function DistanceRequestStartPage({
8970
return transaction.iouRequestType;
9071
}, [transaction?.iouRequestType, selectedTab, lastDistanceExpenseType]);
9172

92-
const prevTransactionReportID = usePrevious(transaction?.reportID);
73+
const resetIOUTypeIfChanged = useResetIOUType({
74+
reportID,
75+
report,
76+
transaction,
77+
isLoadingSelectedTab,
78+
transactionRequestType,
79+
policy,
80+
isTrackDistanceExpense,
81+
});
9382

9483
useEffect(() => {
9584
endSpan(CONST.TELEMETRY.SPAN_OPEN_CREATE_EXPENSE);
@@ -99,60 +88,6 @@ function DistanceRequestStartPage({
9988
Navigation.closeRHPFlow();
10089
};
10190

102-
const resetIOUTypeIfChanged = useCallback(
103-
(newIOUType: IOURequestType) => {
104-
Keyboard.dismiss();
105-
if (transaction?.iouRequestType === newIOUType) {
106-
return;
107-
}
108-
initMoneyRequest({
109-
reportID,
110-
policy,
111-
personalPolicy,
112-
isFromGlobalCreate,
113-
isTrackDistanceExpense,
114-
isFromFloatingActionButton: transaction?.isFromFloatingActionButton ?? transaction?.isFromGlobalCreate ?? isFromGlobalCreate,
115-
currentIouRequestType: transaction?.iouRequestType,
116-
newIouRequestType: newIOUType,
117-
report,
118-
parentReport,
119-
currentDate,
120-
lastSelectedDistanceRates,
121-
currentUserPersonalDetails,
122-
hasOnlyPersonalPolicies,
123-
draftTransactionIDs,
124-
});
125-
},
126-
[
127-
transaction?.iouRequestType,
128-
transaction?.isFromGlobalCreate,
129-
transaction?.isFromFloatingActionButton,
130-
reportID,
131-
policy,
132-
personalPolicy,
133-
isFromGlobalCreate,
134-
report,
135-
parentReport,
136-
currentDate,
137-
lastSelectedDistanceRates,
138-
currentUserPersonalDetails,
139-
isTrackDistanceExpense,
140-
hasOnlyPersonalPolicies,
141-
draftTransactionIDs,
142-
],
143-
);
144-
145-
// Clear out the temporary expense if the reportID in the URL has changed from the transaction's reportID.
146-
useFocusEffect(
147-
useCallback(() => {
148-
// The test transaction can change the reportID of the transaction on the flow so we should prevent the reportID from being reverted again.
149-
if (transaction?.reportID === reportID || isLoadingSelectedTab || !transactionRequestType || prevTransactionReportID !== transaction?.reportID) {
150-
return;
151-
}
152-
resetIOUTypeIfChanged(transactionRequestType);
153-
}, [transaction?.reportID, reportID, resetIOUTypeIfChanged, transactionRequestType, isLoadingSelectedTab, prevTransactionReportID]),
154-
);
155-
15691
const [headerWithBackBtnContainerElement, setHeaderWithBackButtonContainerElement] = useState<HTMLElement | null>(null);
15792
const [tabBarContainerElement, setTabBarContainerElement] = useState<HTMLElement | null>(null);
15893
const [activeTabContainerElement, setActiveTabContainerElement] = useState<HTMLElement | null>(null);
@@ -167,7 +102,6 @@ function DistanceRequestStartPage({
167102
iouType={iouType}
168103
policyID={policy?.id}
169104
accessVariants={[CONST.IOU.ACCESS_VARIANTS.CREATE]}
170-
allPolicies={allPolicies}
171105
>
172106
<ScreenWrapper
173107
shouldEnableKeyboardAvoidingView={selectedTab === CONST.TAB_REQUEST.DISTANCE_ODOMETER}

0 commit comments

Comments
 (0)