Skip to content

Commit 1ffa9e4

Browse files
authored
Merge pull request Expensify#76364 from callstack-internal/feat/replace-perf-5-rule-with-eslint
[Lint] Replace PERF-5 ai reviewer rule with `no-deep-equal-in-memo` eslint equivalent
2 parents 5a706aa + 10708d8 commit 1ffa9e4

13 files changed

Lines changed: 40 additions & 25 deletions

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@
315315
"electron-builder": "26.0.19",
316316
"eslint": "^9.36.0",
317317
"eslint-config-airbnb-typescript": "^18.0.0",
318-
"eslint-config-expensify": "2.0.100",
318+
"eslint-config-expensify": "2.0.101",
319319
"eslint-config-prettier": "^9.1.0",
320320
"eslint-plugin-jest": "^29.0.1",
321321
"eslint-plugin-jsdoc": "^60.7.0",

src/components/MoneyRequestConfirmationList.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,7 +1211,7 @@ MoneyRequestConfirmationList.displayName = 'MoneyRequestConfirmationList';
12111211
export default memo(
12121212
MoneyRequestConfirmationList,
12131213
(prevProps, nextProps) =>
1214-
deepEqual(prevProps.transaction, nextProps.transaction) &&
1214+
prevProps.transaction === nextProps.transaction &&
12151215
prevProps.onSendMoney === nextProps.onSendMoney &&
12161216
prevProps.onConfirm === nextProps.onConfirm &&
12171217
prevProps.iouType === nextProps.iouType &&
@@ -1224,8 +1224,9 @@ export default memo(
12241224
prevProps.isEditingSplitBill === nextProps.isEditingSplitBill &&
12251225
prevProps.iouCurrencyCode === nextProps.iouCurrencyCode &&
12261226
prevProps.iouMerchant === nextProps.iouMerchant &&
1227+
// eslint-disable-next-line rulesdir/no-deep-equal-in-memo -- selectedParticipants is derived with .map() which creates new array references
12271228
deepEqual(prevProps.selectedParticipants, nextProps.selectedParticipants) &&
1228-
deepEqual(prevProps.payeePersonalDetails, nextProps.payeePersonalDetails) &&
1229+
prevProps.payeePersonalDetails === nextProps.payeePersonalDetails &&
12291230
prevProps.isReadOnly === nextProps.isReadOnly &&
12301231
prevProps.policyID === nextProps.policyID &&
12311232
prevProps.reportID === nextProps.reportID &&
@@ -1238,6 +1239,6 @@ export default memo(
12381239
prevProps.onToggleBillable === nextProps.onToggleBillable &&
12391240
prevProps.hasSmartScanFailed === nextProps.hasSmartScanFailed &&
12401241
prevProps.reportActionID === nextProps.reportActionID &&
1241-
deepEqual(prevProps.action, nextProps.action) &&
1242+
prevProps.action === nextProps.action &&
12421243
prevProps.shouldDisplayReceipt === nextProps.shouldDisplayReceipt,
12431244
);

src/components/MoneyRequestConfirmationListFooter.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,7 +1071,7 @@ MoneyRequestConfirmationListFooter.displayName = 'MoneyRequestConfirmationListFo
10711071
export default memo(
10721072
MoneyRequestConfirmationListFooter,
10731073
(prevProps, nextProps) =>
1074-
deepEqual(prevProps.action, nextProps.action) &&
1074+
prevProps.action === nextProps.action &&
10751075
prevProps.currency === nextProps.currency &&
10761076
prevProps.didConfirm === nextProps.didConfirm &&
10771077
prevProps.distance === nextProps.distance &&
@@ -1093,21 +1093,22 @@ export default memo(
10931093
prevProps.isReadOnly === nextProps.isReadOnly &&
10941094
prevProps.isTypeInvoice === nextProps.isTypeInvoice &&
10951095
prevProps.onToggleBillable === nextProps.onToggleBillable &&
1096-
deepEqual(prevProps.policy, nextProps.policy) &&
1097-
deepEqual(prevProps.policyTagLists, nextProps.policyTagLists) &&
1096+
prevProps.policy === nextProps.policy &&
1097+
prevProps.policyTagLists === nextProps.policyTagLists &&
10981098
prevProps.rate === nextProps.rate &&
10991099
prevProps.receiptFilename === nextProps.receiptFilename &&
11001100
prevProps.receiptPath === nextProps.receiptPath &&
11011101
prevProps.reportActionID === nextProps.reportActionID &&
11021102
prevProps.reportID === nextProps.reportID &&
1103+
// eslint-disable-next-line rulesdir/no-deep-equal-in-memo -- selectedParticipants is derived with .map() which creates new array references
11031104
deepEqual(prevProps.selectedParticipants, nextProps.selectedParticipants) &&
11041105
prevProps.shouldDisplayFieldError === nextProps.shouldDisplayFieldError &&
11051106
prevProps.shouldDisplayReceipt === nextProps.shouldDisplayReceipt &&
11061107
prevProps.shouldShowCategories === nextProps.shouldShowCategories &&
11071108
prevProps.shouldShowMerchant === nextProps.shouldShowMerchant &&
11081109
prevProps.shouldShowSmartScanFields === nextProps.shouldShowSmartScanFields &&
11091110
prevProps.shouldShowTax === nextProps.shouldShowTax &&
1110-
deepEqual(prevProps.transaction, nextProps.transaction) &&
1111+
prevProps.transaction === nextProps.transaction &&
11111112
prevProps.transactionID === nextProps.transactionID &&
11121113
prevProps.unit === nextProps.unit,
11131114
);

src/components/OptionRow.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {deepEqual} from 'fast-equals';
22
import React, {useEffect, useRef, useState} from 'react';
33
import type {StyleProp, TextStyle, ViewStyle} from 'react-native';
44
import {InteractionManager, StyleSheet, View} from 'react-native';
5+
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
56
import useLocalize from '@hooks/useLocalize';
67
import useStyleUtils from '@hooks/useStyleUtils';
78
import useTheme from '@hooks/useTheme';
@@ -14,7 +15,6 @@ import Button from './Button';
1415
import DisplayNames from './DisplayNames';
1516
import Hoverable from './Hoverable';
1617
import Icon from './Icon';
17-
import * as Expensicons from './Icon/Expensicons';
1818
import MoneyRequestAmountInput from './MoneyRequestAmountInput';
1919
import OfflineWithFeedback from './OfflineWithFeedback';
2020
import PressableWithFeedback from './Pressable/PressableWithFeedback';
@@ -112,6 +112,7 @@ function OptionRow({
112112
const styles = useThemeStyles();
113113
const StyleUtils = useStyleUtils();
114114
const {translate, localeCompare} = useLocalize();
115+
const icons = useMemoizedLazyExpensifyIcons(['DotIndicator', 'Checkmark'] as const);
115116
const pressableRef = useRef<View | HTMLDivElement>(null);
116117
const [isDisabled, setIsDisabled] = useState(isOptionDisabled);
117118

@@ -282,15 +283,15 @@ function OptionRow({
282283
{!isSelected && option.brickRoadIndicator === CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR && (
283284
<View style={[styles.alignItemsCenter, styles.justifyContentCenter]}>
284285
<Icon
285-
src={Expensicons.DotIndicator}
286+
src={icons.DotIndicator}
286287
fill={theme.danger}
287288
/>
288289
</View>
289290
)}
290291
{!isSelected && option.brickRoadIndicator === CONST.BRICK_ROAD_INDICATOR_STATUS.INFO && (
291292
<View style={[styles.alignItemsCenter, styles.justifyContentCenter]}>
292293
<Icon
293-
src={Expensicons.DotIndicator}
294+
src={icons.DotIndicator}
294295
fill={theme.iconSuccessFill}
295296
/>
296297
</View>
@@ -321,7 +322,7 @@ function OptionRow({
321322
{isSelected && highlightSelected && (
322323
<View style={styles.defaultCheckmarkWrapper}>
323324
<Icon
324-
src={Expensicons.Checkmark}
325+
src={icons.Checkmark}
325326
fill={theme.iconSuccessFill}
326327
/>
327328
</View>
@@ -361,6 +362,7 @@ export default React.memo(
361362
prevProps.showSelectedState === nextProps.showSelectedState &&
362363
prevProps.highlightSelected === nextProps.highlightSelected &&
363364
prevProps.showTitleTooltip === nextProps.showTitleTooltip &&
365+
// eslint-disable-next-line rulesdir/no-deep-equal-in-memo -- icons array is created inline in some usages (e.g., BaseReactionList) with unstable references
364366
deepEqual(prevProps.option.icons, nextProps.option.icons) &&
365367
prevProps.optionIsFocused === nextProps.optionIsFocused &&
366368
prevProps.option.text === nextProps.option.text &&
@@ -373,6 +375,7 @@ export default React.memo(
373375
prevProps.option.pendingAction === nextProps.option.pendingAction &&
374376
prevProps.option.customIcon === nextProps.option.customIcon &&
375377
prevProps.option.tabIndex === nextProps.option.tabIndex &&
378+
// eslint-disable-next-line rulesdir/no-deep-equal-in-memo -- amountInputProps origin and reference stability cannot be determined across all usages
376379
deepEqual(prevProps.option.amountInputProps, nextProps.option.amountInputProps),
377380
);
378381

src/components/PopoverMenu.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,12 +588,15 @@ PopoverMenu.displayName = 'PopoverMenu';
588588
export default React.memo(
589589
PopoverMenu,
590590
(prevProps, nextProps) =>
591+
// eslint-disable-next-line rulesdir/no-deep-equal-in-memo -- menuItems array is created inline in most usages with unstable references
591592
deepEqual(prevProps.menuItems, nextProps.menuItems) &&
592593
prevProps.isVisible === nextProps.isVisible &&
594+
// eslint-disable-next-line rulesdir/no-deep-equal-in-memo -- anchorPosition object is created inline in most usages
593595
deepEqual(prevProps.anchorPosition, nextProps.anchorPosition) &&
594596
prevProps.anchorRef === nextProps.anchorRef &&
595597
prevProps.headerText === nextProps.headerText &&
596598
prevProps.fromSidebarMediumScreen === nextProps.fromSidebarMediumScreen &&
599+
// eslint-disable-next-line rulesdir/no-deep-equal-in-memo -- anchorAlignment object is created inline in most usages
597600
deepEqual(prevProps.anchorAlignment, nextProps.anchorAlignment) &&
598601
prevProps.animationIn === nextProps.animationIn &&
599602
prevProps.animationOut === nextProps.animationOut &&

src/pages/home/ReportScreen.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,4 +1041,5 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
10411041
}
10421042

10431043
ReportScreen.displayName = 'ReportScreen';
1044+
// eslint-disable-next-line rulesdir/no-deep-equal-in-memo
10441045
export default memo(ReportScreen, (prevProps, nextProps) => deepEqual(prevProps.route, nextProps.route));

src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,7 @@ function BaseReportActionContextMenu({
454454
);
455455
}
456456

457+
// eslint-disable-next-line rulesdir/no-deep-equal-in-memo
457458
export default memo(BaseReportActionContextMenu, deepEqual);
458459

459460
export type {BaseReportActionContextMenuProps};

src/pages/home/report/PureReportActionItem.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable rulesdir/no-deep-equal-in-memo */
12
import {deepEqual} from 'fast-equals';
23
import mapValues from 'lodash/mapValues';
34
import React, {memo, use, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';

src/pages/home/report/ReportActionItemContentCreated.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import {deepEqual} from 'fast-equals';
21
import React, {memo, useMemo} from 'react';
32
import {View} from 'react-native';
43
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
@@ -216,8 +215,8 @@ ReportActionItemContentCreated.displayName = 'ReportActionItemContentCreated';
216215
export default memo(
217216
ReportActionItemContentCreated,
218217
(prevProps, nextProps) =>
219-
deepEqual(prevProps.contextValue, nextProps.contextValue) &&
220-
deepEqual(prevProps.parentReportAction, nextProps.parentReportAction) &&
218+
prevProps.contextValue === nextProps.contextValue &&
219+
prevProps.parentReportAction === nextProps.parentReportAction &&
221220
prevProps.transactionID === nextProps.transactionID &&
222221
prevProps.draftMessage === nextProps.draftMessage &&
223222
prevProps.shouldHideThreadDividerLine === nextProps.shouldHideThreadDividerLine,

0 commit comments

Comments
 (0)