Skip to content

Commit 543dddb

Browse files
authored
Merge pull request Expensify#82664 from callstack-internal/perf/report-name-value-pairs-selectors
[No QA] perf: remove ReportNameValuePairs selectors
2 parents 1a0aa79 + 6b07d17 commit 543dddb

8 files changed

Lines changed: 98 additions & 66 deletions

File tree

src/hooks/useParticipantsInvoiceReport.ts

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,12 @@
1-
import {createReportNameValuePairsSelector} from '@selectors/ReportNameValuePairs';
21
import {useMemo} from 'react';
32
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
43
import {isArchivedNonExpenseReport, isArchivedReport, isInvoiceRoom} from '@libs/ReportUtils';
5-
import CONST from '@src/CONST';
64
import ONYXKEYS from '@src/ONYXKEYS';
7-
import type {Report, ReportNameValuePairs} from '@src/types/onyx';
5+
import type {Report} from '@src/types/onyx';
86
import type {InvoiceReceiverType} from '@src/types/onyx/Report';
97
import mapOnyxCollectionItems from '@src/utils/mapOnyxCollectionItems';
108
import useOnyx from './useOnyx';
119

12-
type ReportNameValuePairsSelector = Pick<ReportNameValuePairs, 'private_isArchived'>;
13-
14-
/*
15-
Since invoice chat rooms have `type === CONST.REPORT.TYPE.CHAT`,
16-
we filter on that value to minimize the number of rNVPs being subscribed to.
17-
*/
18-
const reportNameValuePairSelector = (reportNameValuePairs: OnyxEntry<ReportNameValuePairs>): ReportNameValuePairsSelector | undefined => {
19-
if (reportNameValuePairs && 'type' in reportNameValuePairs && reportNameValuePairs?.type !== CONST.REPORT.TYPE.CHAT) {
20-
return;
21-
}
22-
return (
23-
reportNameValuePairs &&
24-
({
25-
private_isArchived: reportNameValuePairs.private_isArchived,
26-
} as ReportNameValuePairsSelector)
27-
);
28-
};
29-
30-
const reportNameValuePairsSelector = (reportNameValuePairs: OnyxCollection<ReportNameValuePairs>) => createReportNameValuePairsSelector(reportNameValuePairs, reportNameValuePairSelector);
31-
3210
const reportSelector = (report: OnyxEntry<Report>): Report | undefined => {
3311
if (!report || !isInvoiceRoom(report)) {
3412
return;
@@ -41,9 +19,8 @@ const allInvoiceReportsSelector = (reports: OnyxCollection<Report>) => mapOnyxCo
4119

4220
function useParticipantsInvoiceReport(receiverID: string | number | undefined, receiverType: InvoiceReceiverType, policyID?: string): OnyxEntry<Report> {
4321
const [allInvoiceReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: true, selector: allInvoiceReportsSelector});
44-
const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}`, {
22+
const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, {
4523
canBeMissing: true,
46-
selector: reportNameValuePairsSelector,
4724
});
4825

4926
const invoiceReport = useMemo(() => {
Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
1-
import type {PrivateIsArchivedMap} from '@selectors/ReportNameValuePairs';
2-
import {privateIsArchivedMapSelector} from '@selectors/ReportNameValuePairs';
31
import ONYXKEYS from '@src/ONYXKEYS';
4-
import {getEmptyObject} from '@src/types/utils/EmptyObject';
5-
import useDeepCompareRef from './useDeepCompareRef';
62
import useOnyx from './useOnyx';
73

4+
type PrivateIsArchivedMap = Record<string, string | undefined>;
5+
86
/**
97
* Hook that returns a map of report IDs to their private_isArchived values
108
*/
119
function usePrivateIsArchivedMap(): PrivateIsArchivedMap {
12-
const [privateIsArchivedMap = getEmptyObject<PrivateIsArchivedMap>()] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, {
10+
const [allReportNVP] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, {
1311
canBeMissing: true,
14-
selector: privateIsArchivedMapSelector,
1512
});
1613

17-
// useDeepCompareRef prevents unnecessary rerenders when the object has same content but different reference.
18-
// The selector always returns a new object instance, and useMemo cannot compare by value.
19-
return useDeepCompareRef(privateIsArchivedMap) ?? {};
14+
const map: PrivateIsArchivedMap = {};
15+
if (allReportNVP) {
16+
for (const [key, value] of Object.entries(allReportNVP)) {
17+
map[key] = value?.private_isArchived;
18+
}
19+
}
20+
return map;
2021
}
2122

2223
export default usePrivateIsArchivedMap;
24+
export type {PrivateIsArchivedMap};

src/libs/OptionsListUtils/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ import {isScanning} from '@libs/TransactionUtils';
149149
import {generateAccountID} from '@libs/UserUtils';
150150
import CONST from '@src/CONST';
151151
import ONYXKEYS from '@src/ONYXKEYS';
152-
import type {PrivateIsArchivedMap} from '@src/selectors/ReportNameValuePairs';
152+
import type {PrivateIsArchivedMap} from '@hooks/usePrivateIsArchivedMap';
153153
import type {
154154
Beta,
155155
DismissedProductTraining,

src/selectors/ReportNameValuePairs.ts

Lines changed: 0 additions & 28 deletions
This file was deleted.

tests/perf-test/OptionsListUtils.perf-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {rand} from '@ngneat/falso';
22
import type * as NativeNavigation from '@react-navigation/native';
3-
import type {PrivateIsArchivedMap} from '@selectors/ReportNameValuePairs';
3+
import type {PrivateIsArchivedMap} from '@hooks/usePrivateIsArchivedMap';
44
import Onyx from 'react-native-onyx';
55
import {measureFunction} from 'reassure';
66
import {createOptionList, filterAndOrderOptions, getMemberInviteOptions, getSearchOptions, getValidOptions} from '@libs/OptionsListUtils';

tests/perf-test/SearchRouter.perf-test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type * as NativeNavigation from '@react-navigation/native';
2-
import type {PrivateIsArchivedMap} from '@selectors/ReportNameValuePairs';
2+
import type {PrivateIsArchivedMap} from '@hooks/usePrivateIsArchivedMap';
33
import {fireEvent, screen} from '@testing-library/react-native';
44
import React, {useMemo} from 'react';
55
import Onyx from 'react-native-onyx';

tests/unit/OptionsListUtilsTest.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable @typescript-eslint/naming-convention */
2-
import type {PrivateIsArchivedMap} from '@selectors/ReportNameValuePairs';
2+
import type {PrivateIsArchivedMap} from '@hooks/usePrivateIsArchivedMap';
33
import {act, render, renderHook} from '@testing-library/react-native';
44
import {View} from 'react-native';
55
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import {act, renderHook} from '@testing-library/react-native';
2+
import Onyx from 'react-native-onyx';
3+
import usePrivateIsArchivedMap from '@hooks/usePrivateIsArchivedMap';
4+
import ONYXKEYS from '@src/ONYXKEYS';
5+
import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
6+
7+
const REPORT_ID_1 = 'report_1';
8+
const REPORT_ID_2 = 'report_2';
9+
const REPORT_ID_3 = 'report_3';
10+
const ARCHIVED_DATE = '2024-01-15';
11+
const SECOND_ARCHIVED_DATE = '2024-03-22';
12+
13+
describe('usePrivateIsArchivedMap', () => {
14+
beforeEach(() => {
15+
Onyx.init({keys: ONYXKEYS});
16+
});
17+
18+
afterEach(async () => {
19+
await act(async () => {
20+
await Onyx.clear();
21+
});
22+
await waitForBatchedUpdatesWithAct();
23+
});
24+
25+
it('returns an empty map when no report name value pairs are set', () => {
26+
const {result} = renderHook(() => usePrivateIsArchivedMap());
27+
expect(result.current).toEqual({});
28+
});
29+
30+
it('maps report keys to their private_isArchived values', async () => {
31+
await act(async () => {
32+
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${REPORT_ID_1}`, {private_isArchived: ARCHIVED_DATE});
33+
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${REPORT_ID_2}`, {private_isArchived: SECOND_ARCHIVED_DATE});
34+
});
35+
36+
const {result} = renderHook(() => usePrivateIsArchivedMap());
37+
38+
expect(result.current[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${REPORT_ID_1}`]).toBe(ARCHIVED_DATE);
39+
expect(result.current[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${REPORT_ID_2}`]).toBe(SECOND_ARCHIVED_DATE);
40+
});
41+
42+
it('returns undefined for reports that are not archived', async () => {
43+
await act(async () => {
44+
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${REPORT_ID_3}`, {origin: 'email'});
45+
});
46+
47+
const {result} = renderHook(() => usePrivateIsArchivedMap());
48+
49+
expect(result.current[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${REPORT_ID_3}`]).toBeUndefined();
50+
});
51+
52+
it('updates the map when Onyx data changes', async () => {
53+
await act(async () => {
54+
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${REPORT_ID_1}`, {origin: 'email'});
55+
});
56+
57+
const {result} = renderHook(() => usePrivateIsArchivedMap());
58+
59+
expect(result.current[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${REPORT_ID_1}`]).toBeUndefined();
60+
61+
await act(async () => {
62+
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${REPORT_ID_1}`, {private_isArchived: ARCHIVED_DATE});
63+
});
64+
65+
await waitForBatchedUpdatesWithAct();
66+
67+
expect(result.current[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${REPORT_ID_1}`]).toBe(ARCHIVED_DATE);
68+
});
69+
70+
it('handles a mix of archived and non-archived reports', async () => {
71+
await act(async () => {
72+
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${REPORT_ID_1}`, {private_isArchived: ARCHIVED_DATE});
73+
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${REPORT_ID_2}`, {origin: 'email'});
74+
});
75+
76+
const {result} = renderHook(() => usePrivateIsArchivedMap());
77+
78+
expect(result.current[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${REPORT_ID_1}`]).toBe(ARCHIVED_DATE);
79+
expect(result.current[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${REPORT_ID_2}`]).toBeUndefined();
80+
});
81+
});

0 commit comments

Comments
 (0)