Skip to content

Commit da93289

Browse files
authored
Merge pull request Expensify#84601 from callstack-internal/perf/remove-draft-transactions-collection-subscriptions
perf: remove draftTransactions collection subscriptions from expense creation flow
2 parents 7563a9a + 3c28125 commit da93289

9 files changed

Lines changed: 42 additions & 46 deletions

File tree

src/components/TestDrive/Modal/EmployeeTestDriveModal.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {useRoute} from '@react-navigation/native';
2+
import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft';
23
import {format} from 'date-fns';
34
import {Str} from 'expensify-common';
45
import React, {useCallback, useMemo, useState} from 'react';
@@ -49,8 +50,8 @@ function EmployeeTestDriveModal() {
4950
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
5051
const personalPolicy = usePersonalPolicy();
5152
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
52-
const [draftTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT);
5353
const hasOnlyPersonalPolicies = useMemo(() => hasOnlyPersonalPoliciesUtil(allPolicies), [allPolicies]);
54+
const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector});
5455

5556
const onBossEmailChange = useCallback((value: string) => {
5657
setBossEmail(value);
@@ -86,7 +87,7 @@ function EmployeeTestDriveModal() {
8687
currentDate,
8788
currentUserPersonalDetails,
8889
hasOnlyPersonalPolicies,
89-
draftTransactions,
90+
draftTransactionIDs,
9091
});
9192

9293
setMoneyRequestReceipt(transactionID, source, filename, true, CONST.TEST_RECEIPT.FILE_TYPE, false, true);

src/hooks/useReceiptScanDrop.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft';
12
import React, {useMemo} from 'react';
23
import {setTransactionReport} from '@libs/actions/Transaction';
34
import {navigateToParticipantPage} from '@libs/IOUUtils';
@@ -35,7 +36,7 @@ function useReceiptScanDrop() {
3536
const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`);
3637
const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID);
3738
const [personalPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${personalPolicyID}`);
38-
const [draftTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT);
39+
const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector});
3940

4041
// Memoize the new report ID to avoid re-generating it on every render and cause the hook to change, which leads to performance issues.
4142
const newReportID = useMemo(() => generateReportID(), []);
@@ -52,7 +53,7 @@ function useReceiptScanDrop() {
5253
currentDate,
5354
currentUserPersonalDetails,
5455
hasOnlyPersonalPolicies,
55-
draftTransactions,
56+
draftTransactionIDs,
5657
});
5758

5859
const newReceiptFiles: ReceiptFile[] = [];

src/libs/actions/IOU/index.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ type InitMoneyRequestParams = {
297297
lastSelectedDistanceRates?: OnyxEntry<OnyxTypes.LastSelectedDistanceRates>;
298298
currentUserPersonalDetails: CurrentUserPersonalDetails;
299299
hasOnlyPersonalPolicies: boolean;
300-
draftTransactions: OnyxCollection<OnyxTypes.Transaction>;
300+
draftTransactionIDs?: string[];
301301
};
302302

303303
type MoneyRequestInformation = {
@@ -1262,7 +1262,7 @@ function initMoneyRequest({
12621262
lastSelectedDistanceRates,
12631263
currentUserPersonalDetails,
12641264
hasOnlyPersonalPolicies,
1265-
draftTransactions,
1265+
draftTransactionIDs,
12661266
}: InitMoneyRequestParams) {
12671267
// Generate a brand new transactionID
12681268
const newTransactionID = CONST.IOU.OPTIMISTIC_TRANSACTION_ID;
@@ -1271,7 +1271,12 @@ function initMoneyRequest({
12711271
const created = currentDate ?? format(new Date(), 'yyyy-MM-dd');
12721272

12731273
// We remove draft transactions created during multi scanning if there are some
1274-
removeDraftTransactions(true, draftTransactions);
1274+
if (draftTransactionIDs) {
1275+
const idsToRemove = draftTransactionIDs.filter((id) => id !== CONST.IOU.OPTIMISTIC_TRANSACTION_ID);
1276+
removeDraftTransactionsByIDs(idsToRemove);
1277+
} else {
1278+
removeDraftTransactions(true);
1279+
}
12751280

12761281
// in case we have to re-init money request, but the IOU request type is the same with the old draft transaction,
12771282
// we should keep most of the existing data by using the ONYX MERGE operation

src/pages/Share/SubmitDetailsPage.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,6 @@ function SubmitDetailsPage({
9191
const fileName = shouldUsePreValidatedFile ? getFileName(validFilesToUpload?.uri ?? CONST.ATTACHMENT_IMAGE_DEFAULT_NAME) : getFileName(currentAttachment?.content ?? '');
9292
const fileType = shouldUsePreValidatedFile ? (validFilesToUpload?.type ?? CONST.RECEIPT_ALLOWED_FILE_TYPES.JPEG) : (currentAttachment?.mimeType ?? '');
9393
const [hasOnlyPersonalPolicies = false] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: hasOnlyPersonalPoliciesUtil});
94-
const [draftTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT);
95-
9694
useEffect(() => {
9795
if (!errorTitle || !errorMessage) {
9896
return;
@@ -113,9 +111,9 @@ function SubmitDetailsPage({
113111
currentDate,
114112
currentUserPersonalDetails,
115113
hasOnlyPersonalPolicies,
116-
draftTransactions,
114+
draftTransactionIDs,
117115
});
118-
// The draftTransactions can be changed if users update the expense, so we don't want to re-init the money request
116+
// initMoneyRequest is an imported action, intentionally excluded to avoid re-initializing on every render
119117
// eslint-disable-next-line react-hooks/exhaustive-deps
120118
}, [reportOrAccountID, policy, personalPolicy, report, parentReport, currentDate, currentUserPersonalDetails, hasOnlyPersonalPolicies]);
121119

src/pages/inbox/report/ReportActionCompose/useAttachmentUploadValidation.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft';
12
import {useCallback, useContext, useMemo, useRef} from 'react';
23
import type {OnyxEntry} from 'react-native-onyx';
34
import useFilesValidation from '@hooks/useFilesValidation';
@@ -56,9 +57,9 @@ function useAttachmentUploadValidation({
5657
const [ownerBillingGraceEndPeriod] = useOnyx(ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END);
5758
const personalPolicy = usePersonalPolicy();
5859
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
59-
const [draftTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT);
6060
const [userBillingGraceEndPeriods] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END);
6161
const hasOnlyPersonalPolicies = useMemo(() => hasOnlyPersonalPoliciesUtil(allPolicies), [allPolicies]);
62+
const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector});
6263

6364
const reportAttachmentsContext = useContext(AttachmentModalContext);
6465
const showAttachmentModalScreen = useCallback(
@@ -104,7 +105,7 @@ function useAttachmentUploadValidation({
104105
currentDate,
105106
currentUserPersonalDetails,
106107
hasOnlyPersonalPolicies,
107-
draftTransactions,
108+
draftTransactionIDs,
108109
});
109110

110111
for (const [index, file] of files.entries()) {

src/pages/iou/request/DistanceRequestStartPage.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {useFocusEffect} from '@react-navigation/native';
2+
import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft';
23
import React, {useCallback, useEffect, useMemo, useState} from 'react';
34
import {Keyboard, View} from 'react-native';
45
import FocusTrapContainerElement from '@components/FocusTrap/FocusTrapContainerElement';
@@ -57,7 +58,6 @@ function DistanceRequestStartPage({
5758
const [lastDistanceExpenseType] = useOnyx(ONYXKEYS.NVP_LAST_DISTANCE_EXPENSE_TYPE);
5859
const isLoadingSelectedTab = isLoadingOnyxValue(selectedTabResult);
5960
const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${getNonEmptyStringOnyxID(route?.params.transactionID)}`);
60-
const [draftTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT);
6161
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
6262
const [lastSelectedDistanceRates] = useOnyx(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES);
6363
const [currentDate] = useOnyx(ONYXKEYS.CURRENT_DATE);
@@ -67,6 +67,7 @@ function DistanceRequestStartPage({
6767
const hasOnlyPersonalPolicies = useMemo(() => hasOnlyPersonalPoliciesUtil(allPolicies), [allPolicies]);
6868
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
6969
const personalPolicy = usePersonalPolicy();
70+
const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector});
7071

7172
const tabTitles = {
7273
[CONST.IOU.TYPE.REQUEST]: translate('iou.trackDistance'),
@@ -120,7 +121,7 @@ function DistanceRequestStartPage({
120121
lastSelectedDistanceRates,
121122
currentUserPersonalDetails,
122123
hasOnlyPersonalPolicies,
123-
draftTransactions,
124+
draftTransactionIDs,
124125
});
125126
},
126127
[
@@ -137,7 +138,7 @@ function DistanceRequestStartPage({
137138
lastSelectedDistanceRates,
138139
currentUserPersonalDetails,
139140
hasOnlyPersonalPolicies,
140-
draftTransactions,
141+
draftTransactionIDs,
141142
],
142143
);
143144

src/pages/iou/request/IOURequestStartPage.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {useFocusEffect} from '@react-navigation/native';
22
import {iouRequestPolicyCollectionSelector} from '@selectors/Policy';
3+
import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft';
34
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
45
import {Keyboard, View} from 'react-native';
56
import type {OnyxEntry} from 'react-native-onyx';
@@ -47,7 +48,7 @@ import type SCREENS from '@src/SCREENS';
4748
import type {DismissedProductTraining, SelectedTabRequest} from '@src/types/onyx';
4849
import {isEmptyObject} from '@src/types/utils/EmptyObject';
4950
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
50-
import IOURequestStepAmount from './step/IOURequestStepAmount';
51+
import {IOURequestStepAmountWithTransactionOnly} from './step/IOURequestStepAmount';
5152
import IOURequestStepDestination from './step/IOURequestStepDestination';
5253
import IOURequestStepDistance from './step/IOURequestStepDistance';
5354
import IOURequestStepHours from './step/IOURequestStepHours';
@@ -83,6 +84,7 @@ function IOURequestStartPage({
8384
const shouldUseTab = iouType !== CONST.IOU.TYPE.SEND && iouType !== CONST.IOU.TYPE.PAY && iouType !== CONST.IOU.TYPE.INVOICE;
8485
const personalPolicy = usePersonalPolicy();
8586
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
87+
const [reportDraft] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${reportID}`);
8688
const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`);
8789
const policy = usePolicy(report?.policyID);
8890
const [lastSelectedTab, selectedTabResult] = useOnyx(`${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.IOU_REQUEST_TYPE}`);
@@ -97,11 +99,11 @@ function IOURequestStartPage({
9799
});
98100

99101
const [lastSelectedDistanceRates] = useOnyx(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES);
100-
const [draftTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT);
101102
const [isMultiScanEnabled, setIsMultiScanEnabled] = useState(false);
102103
const [currentDate] = useOnyx(ONYXKEYS.CURRENT_DATE);
103104
const {isOffline} = useNetwork();
104105
const [hasUserSubmittedExpenseOrScannedReceipt] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, {selector: isTestReceiptTooltipDismissedSelector});
106+
const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector});
105107
const hasOnlyPersonalPolicies = useMemo(() => hasOnlyPersonalPoliciesUtil(allPolicies), [allPolicies]);
106108

107109
const perDiemInputRef = useRef<AnimatedTextInputRef | null>(null);
@@ -201,7 +203,7 @@ function IOURequestStartPage({
201203
lastSelectedDistanceRates,
202204
currentUserPersonalDetails,
203205
hasOnlyPersonalPolicies,
204-
draftTransactions,
206+
draftTransactionIDs,
205207
});
206208
},
207209
[
@@ -218,7 +220,7 @@ function IOURequestStartPage({
218220
lastSelectedDistanceRates,
219221
currentUserPersonalDetails,
220222
hasOnlyPersonalPolicies,
221-
draftTransactions,
223+
draftTransactionIDs,
222224
],
223225
);
224226

@@ -331,10 +333,12 @@ function IOURequestStartPage({
331333
<TopTab.Screen name={CONST.TAB_REQUEST.MANUAL}>
332334
{() => (
333335
<TabScreenWithFocusTrapWrapper>
334-
<IOURequestStepAmount
336+
<IOURequestStepAmountWithTransactionOnly
335337
shouldKeepUserInput
336338
route={route}
337339
navigation={navigation}
340+
report={report}
341+
reportDraft={reportDraft}
338342
/>
339343
</TabScreenWithFocusTrapWrapper>
340344
)}
@@ -415,10 +419,12 @@ function IOURequestStartPage({
415419
onContainerElementChanged={setActiveTabContainerElement}
416420
style={[styles.flexColumn, styles.flex1]}
417421
>
418-
<IOURequestStepAmount
422+
<IOURequestStepAmountWithTransactionOnly
419423
route={route}
420424
navigation={navigation}
421425
shouldKeepUserInput
426+
report={report}
427+
reportDraft={reportDraft}
422428
/>
423429
</FocusTrapContainerElement>
424430
)}

src/pages/iou/request/step/IOURequestStepAmount.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,10 @@ function IOURequestStepAmount({
108108
const defaultExpensePolicy = useDefaultExpensePolicy();
109109
const personalPolicy = usePersonalPolicy();
110110
const [amountOwed] = useOnyx(ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED);
111-
const {duplicateTransactions, duplicateTransactionViolations} = useDuplicateTransactionsAndViolations(transactionID ? [transactionID] : []);
111+
const isEditing = action === CONST.IOU.ACTION.EDIT;
112+
const {duplicateTransactions, duplicateTransactionViolations} = useDuplicateTransactionsAndViolations(isEditing && transactionID ? [transactionID] : []);
112113
const reportAttributesDerived = useReportAttributes();
113114
const privateIsArchivedMap = usePrivateIsArchivedMap();
114-
const isEditing = action === CONST.IOU.ACTION.EDIT;
115115
const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT;
116116
const isCreateAction = action === CONST.IOU.ACTION.CREATE;
117117
const isSubmitAction = action === CONST.IOU.ACTION.SUBMIT;
@@ -130,9 +130,9 @@ function IOURequestStepAmount({
130130
const isUnreportedDistanceExpense = isEditing && isDistanceRequest(transaction) && isExpenseUnreported(transaction);
131131

132132
const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT);
133-
const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS);
134133
const [transactionDrafts] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftsSelector});
135134
const draftTransactionIDs = Object.keys(transactionDrafts ?? {});
135+
const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS);
136136

137137
const currentUserAccountIDParam = currentUserPersonalDetails.accountID;
138138
const currentUserEmailParam = currentUserPersonalDetails.login ?? '';
@@ -491,5 +491,8 @@ const IOURequestStepAmountWithWritableReportOrNotFound = withWritableReportOrNot
491491
// eslint-disable-next-line rulesdir/no-negated-variables
492492
const IOURequestStepAmountWithFullTransactionOrNotFound = withFullTransactionOrNotFound(IOURequestStepAmountWithWritableReportOrNotFound, true);
493493

494+
// Version without withWritableReportOrNotFound, for use when parent already provides report prop
495+
const IOURequestStepAmountWithTransactionOnly = withFullTransactionOrNotFound(IOURequestStepAmount, true);
496+
494497
export default IOURequestStepAmountWithFullTransactionOrNotFound;
495-
export {isParticipantP2P};
498+
export {isParticipantP2P, IOURequestStepAmountWithTransactionOnly};

tests/actions/IOUTest.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11616,7 +11616,6 @@ describe('actions/IOU', () => {
1161611616
currentDate,
1161711617
currentUserPersonalDetails,
1161811618
hasOnlyPersonalPolicies: false,
11619-
draftTransactions: undefined,
1162011619
});
1162111620
})
1162211621
.then(async () => {
@@ -11639,7 +11638,6 @@ describe('actions/IOU', () => {
1163911638
currentDate,
1164011639
currentUserPersonalDetails,
1164111640
hasOnlyPersonalPolicies: false,
11642-
draftTransactions: undefined,
1164311641
});
1164411642
})
1164511643
.then(async () => {
@@ -11663,7 +11661,6 @@ describe('actions/IOU', () => {
1166311661
currentDate,
1166411662
currentUserPersonalDetails,
1166511663
hasOnlyPersonalPolicies: false,
11666-
draftTransactions: undefined,
1166711664
});
1166811665
})
1166911666
.then(async () => {
@@ -11684,10 +11681,6 @@ describe('actions/IOU', () => {
1168411681
// Set up an additional draft transaction
1168511682
await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${otherDraftTransactionID}`, otherDraftTransaction);
1168611683

11687-
const draftTransactions = {
11688-
[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${otherDraftTransactionID}`]: otherDraftTransaction,
11689-
};
11690-
1169111684
await waitForBatchedUpdates()
1169211685
.then(() => {
1169311686
initMoneyRequest({
@@ -11701,7 +11694,6 @@ describe('actions/IOU', () => {
1170111694
currentDate,
1170211695
currentUserPersonalDetails,
1170311696
hasOnlyPersonalPolicies: false,
11704-
draftTransactions,
1170511697
});
1170611698
})
1170711699
.then(async () => {
@@ -11727,11 +11719,6 @@ describe('actions/IOU', () => {
1172711719
await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${otherDraftTransactionID}`, otherDraftTransaction);
1172811720
await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`, existingOptimisticTransaction);
1172911721

11730-
const draftTransactions = {
11731-
[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${otherDraftTransactionID}`]: otherDraftTransaction,
11732-
[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`]: existingOptimisticTransaction,
11733-
};
11734-
1173511722
await waitForBatchedUpdates()
1173611723
.then(() => {
1173711724
initMoneyRequest({
@@ -11745,7 +11732,6 @@ describe('actions/IOU', () => {
1174511732
currentDate,
1174611733
currentUserPersonalDetails,
1174711734
hasOnlyPersonalPolicies: false,
11748-
draftTransactions,
1174911735
});
1175011736
})
1175111737
.then(async () => {
@@ -11772,11 +11758,6 @@ describe('actions/IOU', () => {
1177211758
await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${draftTransactionID1}`, draftTransaction1);
1177311759
await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${draftTransactionID2}`, draftTransaction2);
1177411760

11775-
const draftTransactions = {
11776-
[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${draftTransactionID1}`]: draftTransaction1,
11777-
[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${draftTransactionID2}`]: draftTransaction2,
11778-
};
11779-
1178011761
await waitForBatchedUpdates()
1178111762
.then(() => {
1178211763
initMoneyRequest({
@@ -11790,7 +11771,6 @@ describe('actions/IOU', () => {
1179011771
currentDate,
1179111772
currentUserPersonalDetails,
1179211773
hasOnlyPersonalPolicies: false,
11793-
draftTransactions,
1179411774
});
1179511775
})
1179611776
.then(async () => {

0 commit comments

Comments
 (0)