From 62283a4b612dd65a72b6b1ccec7e96c0b998fad1 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Fri, 16 May 2025 22:31:51 +0530 Subject: [PATCH 1/9] Prefer fast-equals over lodash/isEqual. Signed-off-by: krishna2323 --- .eslintrc.js | 6 +++- .../Attachments/AttachmentCarousel/index.tsx | 4 +-- src/components/Form/FormProvider.tsx | 4 +-- .../MoneyRequestConfirmationList.tsx | 10 +++--- .../MoneyRequestConfirmationListFooter.tsx | 12 +++---- src/components/OptionRow.tsx | 6 ++-- src/components/PopoverMenu.tsx | 8 ++--- src/components/PopoverWithMeasuredContent.tsx | 4 +-- .../SearchPageHeaderInput.tsx | 4 +-- .../Search/SearchRouter/SearchRouter.tsx | 4 +-- src/hooks/useDeepCompareRef.ts | 4 +-- src/hooks/useFetchRoute.ts | 4 +-- src/hooks/useSearchHighlightAndScroll.ts | 6 ++-- src/libs/Performance.tsx | 4 +-- src/libs/ReportUtils.ts | 4 +-- src/libs/TransactionUtils/index.ts | 4 +-- src/libs/actions/PersistedRequests.ts | 6 ++-- src/libs/actions/Transaction.ts | 4 +-- src/pages/home/ReportScreen.tsx | 4 +-- .../BaseReportActionContextMenu.tsx | 4 +-- .../home/report/PureReportActionItem.tsx | 32 +++++++++---------- .../report/ReportActionItemContentCreated.tsx | 6 ++-- src/pages/home/report/ReportFooter.tsx | 10 +++--- .../request/MoneyRequestAttendeeSelector.tsx | 4 +-- .../MoneyRequestParticipantsSelector.tsx | 4 +-- .../request/step/IOURequestStepAttendees.tsx | 4 +-- .../request/step/IOURequestStepDistance.tsx | 8 ++--- src/pages/workspace/WorkspaceMembersPage.tsx | 4 +-- tests/actions/IOUTest.ts | 7 ++-- 29 files changed, 95 insertions(+), 90 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 45d024c1566ed..0b33de33fd164 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -82,9 +82,13 @@ const restrictedImportPaths = [ name: 'lodash/memoize', message: "Please use '@src/libs/memoize' instead.", }, + { + name: 'lodash/isEqual', + message: "Please use 'deepEqual' from 'fast-equals' instead.", + }, { name: 'lodash', - importNames: ['memoize'], + importNames: ['memoize', 'isEqual'], message: "Please use '@src/libs/memoize' instead.", }, { diff --git a/src/components/Attachments/AttachmentCarousel/index.tsx b/src/components/Attachments/AttachmentCarousel/index.tsx index 3c883530df4f7..b3d81544a1bd5 100644 --- a/src/components/Attachments/AttachmentCarousel/index.tsx +++ b/src/components/Attachments/AttachmentCarousel/index.tsx @@ -1,4 +1,4 @@ -import isEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import type {MutableRefObject} from 'react'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import type {ListRenderItemInfo} from 'react-native'; @@ -100,7 +100,7 @@ function AttachmentCarousel({report, attachmentID, source, onNavigate, setDownlo newAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.REPORT, {parentReportAction, reportActions: reportActions ?? undefined, report}); } - if (isEqual(attachments, newAttachments)) { + if (deepEqual(attachments, newAttachments)) { if (attachments.length === 0) { setPage(-1); setDownloadButtonVisibility?.(false); diff --git a/src/components/Form/FormProvider.tsx b/src/components/Form/FormProvider.tsx index 753d55ed92c64..500d9984487ca 100644 --- a/src/components/Form/FormProvider.tsx +++ b/src/components/Form/FormProvider.tsx @@ -1,5 +1,5 @@ import {useFocusEffect} from '@react-navigation/native'; -import lodashIsEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import type {ForwardedRef, MutableRefObject, ReactNode, RefAttributes} from 'react'; import React, {createRef, forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {InteractionManager} from 'react-native'; @@ -171,7 +171,7 @@ function FormProvider( const touchedInputErrors = Object.fromEntries(Object.entries(validateErrors).filter(([inputID]) => touchedInputs.current[inputID])); - if (!lodashIsEqual(errors, touchedInputErrors)) { + if (!deepEqual(errors, touchedInputErrors)) { setErrors(touchedInputErrors); } diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 99f5da95537c9..b982e0aaf45d7 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -1,5 +1,5 @@ import {useFocusEffect, useIsFocused} from '@react-navigation/native'; -import lodashIsEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {InteractionManager, View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; @@ -1118,7 +1118,7 @@ MoneyRequestConfirmationList.displayName = 'MoneyRequestConfirmationList'; export default memo( MoneyRequestConfirmationList, (prevProps, nextProps) => - lodashIsEqual(prevProps.transaction, nextProps.transaction) && + deepEqual(prevProps.transaction, nextProps.transaction) && prevProps.onSendMoney === nextProps.onSendMoney && prevProps.onConfirm === nextProps.onConfirm && prevProps.iouType === nextProps.iouType && @@ -1130,8 +1130,8 @@ export default memo( prevProps.isEditingSplitBill === nextProps.isEditingSplitBill && prevProps.iouCurrencyCode === nextProps.iouCurrencyCode && prevProps.iouMerchant === nextProps.iouMerchant && - lodashIsEqual(prevProps.selectedParticipants, nextProps.selectedParticipants) && - lodashIsEqual(prevProps.payeePersonalDetails, nextProps.payeePersonalDetails) && + deepEqual(prevProps.selectedParticipants, nextProps.selectedParticipants) && + deepEqual(prevProps.payeePersonalDetails, nextProps.payeePersonalDetails) && prevProps.isReadOnly === nextProps.isReadOnly && prevProps.bankAccountRoute === nextProps.bankAccountRoute && prevProps.policyID === nextProps.policyID && @@ -1145,6 +1145,6 @@ export default memo( prevProps.onToggleBillable === nextProps.onToggleBillable && prevProps.hasSmartScanFailed === nextProps.hasSmartScanFailed && prevProps.reportActionID === nextProps.reportActionID && - lodashIsEqual(prevProps.action, nextProps.action) && + deepEqual(prevProps.action, nextProps.action) && prevProps.shouldDisplayReceipt === nextProps.shouldDisplayReceipt, ); diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 15b5cf32d0bdb..e0a57f4b7bcf9 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -1,6 +1,6 @@ import {format} from 'date-fns'; import {Str} from 'expensify-common'; -import lodashIsEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import React, {memo, useMemo, useReducer} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; @@ -940,7 +940,7 @@ MoneyRequestConfirmationListFooter.displayName = 'MoneyRequestConfirmationListFo export default memo( MoneyRequestConfirmationListFooter, (prevProps, nextProps) => - lodashIsEqual(prevProps.action, nextProps.action) && + deepEqual(prevProps.action, nextProps.action) && prevProps.currency === nextProps.currency && prevProps.didConfirm === nextProps.didConfirm && prevProps.distance === nextProps.distance && @@ -963,21 +963,21 @@ export default memo( prevProps.isReadOnly === nextProps.isReadOnly && prevProps.isTypeInvoice === nextProps.isTypeInvoice && prevProps.onToggleBillable === nextProps.onToggleBillable && - lodashIsEqual(prevProps.policy, nextProps.policy) && - lodashIsEqual(prevProps.policyTagLists, nextProps.policyTagLists) && + deepEqual(prevProps.policy, nextProps.policy) && + deepEqual(prevProps.policyTagLists, nextProps.policyTagLists) && prevProps.rate === nextProps.rate && prevProps.receiptFilename === nextProps.receiptFilename && prevProps.receiptPath === nextProps.receiptPath && prevProps.reportActionID === nextProps.reportActionID && prevProps.reportID === nextProps.reportID && - lodashIsEqual(prevProps.selectedParticipants, nextProps.selectedParticipants) && + deepEqual(prevProps.selectedParticipants, nextProps.selectedParticipants) && prevProps.shouldDisplayFieldError === nextProps.shouldDisplayFieldError && prevProps.shouldDisplayReceipt === nextProps.shouldDisplayReceipt && prevProps.shouldShowCategories === nextProps.shouldShowCategories && prevProps.shouldShowMerchant === nextProps.shouldShowMerchant && prevProps.shouldShowSmartScanFields === nextProps.shouldShowSmartScanFields && prevProps.shouldShowTax === nextProps.shouldShowTax && - lodashIsEqual(prevProps.transaction, nextProps.transaction) && + deepEqual(prevProps.transaction, nextProps.transaction) && prevProps.transactionID === nextProps.transactionID && prevProps.unit === nextProps.unit, ); diff --git a/src/components/OptionRow.tsx b/src/components/OptionRow.tsx index f448763bac418..ee7b110d06651 100644 --- a/src/components/OptionRow.tsx +++ b/src/components/OptionRow.tsx @@ -1,4 +1,4 @@ -import lodashIsEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import React, {useEffect, useRef, useState} from 'react'; import type {StyleProp, TextStyle, ViewStyle} from 'react-native'; import {InteractionManager, StyleSheet, View} from 'react-native'; @@ -358,7 +358,7 @@ export default React.memo( prevProps.showSelectedState === nextProps.showSelectedState && prevProps.highlightSelected === nextProps.highlightSelected && prevProps.showTitleTooltip === nextProps.showTitleTooltip && - lodashIsEqual(prevProps.option.icons, nextProps.option.icons) && + deepEqual(prevProps.option.icons, nextProps.option.icons) && prevProps.optionIsFocused === nextProps.optionIsFocused && prevProps.option.text === nextProps.option.text && prevProps.option.alternateText === nextProps.option.alternateText && @@ -370,7 +370,7 @@ export default React.memo( prevProps.option.pendingAction === nextProps.option.pendingAction && prevProps.option.customIcon === nextProps.option.customIcon && prevProps.option.tabIndex === nextProps.option.tabIndex && - lodashIsEqual(prevProps.option.amountInputProps, nextProps.option.amountInputProps), + deepEqual(prevProps.option.amountInputProps, nextProps.option.amountInputProps), ); export type {OptionRowProps}; diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index 902ded4b71f5e..aec8555fed499 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -1,5 +1,5 @@ /* eslint-disable react/jsx-props-no-spreading */ -import lodashIsEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import type {ReactNode, RefObject} from 'react'; import React, {useCallback, useLayoutEffect, useMemo, useState} from 'react'; import {StyleSheet, View} from 'react-native'; @@ -436,13 +436,13 @@ PopoverMenu.displayName = 'PopoverMenu'; export default React.memo( PopoverMenu, (prevProps, nextProps) => - lodashIsEqual(prevProps.menuItems, nextProps.menuItems) && + deepEqual(prevProps.menuItems, nextProps.menuItems) && prevProps.isVisible === nextProps.isVisible && - lodashIsEqual(prevProps.anchorPosition, nextProps.anchorPosition) && + deepEqual(prevProps.anchorPosition, nextProps.anchorPosition) && prevProps.anchorRef === nextProps.anchorRef && prevProps.headerText === nextProps.headerText && prevProps.fromSidebarMediumScreen === nextProps.fromSidebarMediumScreen && - lodashIsEqual(prevProps.anchorAlignment, nextProps.anchorAlignment) && + deepEqual(prevProps.anchorAlignment, nextProps.anchorAlignment) && prevProps.animationIn === nextProps.animationIn && prevProps.animationOut === nextProps.animationOut && prevProps.animationInTiming === nextProps.animationInTiming && diff --git a/src/components/PopoverWithMeasuredContent.tsx b/src/components/PopoverWithMeasuredContent.tsx index 9b4dde37fa10a..cef0ab214f69a 100644 --- a/src/components/PopoverWithMeasuredContent.tsx +++ b/src/components/PopoverWithMeasuredContent.tsx @@ -1,4 +1,4 @@ -import isEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import React, {useMemo, useState} from 'react'; import type {LayoutChangeEvent} from 'react-native'; import {View} from 'react-native'; @@ -197,7 +197,7 @@ export default React.memo(PopoverWithMeasuredContent, (prevProps, nextProps) => if (prevProps.isVisible === nextProps.isVisible && nextProps.isVisible === false) { return true; } - return isEqual(prevProps, nextProps); + return deepEqual(prevProps, nextProps); }); export type {PopoverWithMeasuredContentProps}; diff --git a/src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx b/src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx index 16dcb0177e9db..9b4cfb697cf12 100644 --- a/src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx +++ b/src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx @@ -1,5 +1,5 @@ import {useIsFocused} from '@react-navigation/native'; -import isEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; @@ -156,7 +156,7 @@ function SearchPageHeaderInput({queryJSON, searchRouterListVisible, hideSearchRo setAutocompleteQueryValue(updatedUserQuery); const updatedSubstitutionsMap = getUpdatedSubstitutionsMap(singleLineUserQuery, autocompleteSubstitutions); - if (!isEqual(autocompleteSubstitutions, updatedSubstitutionsMap)) { + if (!deepEqual(autocompleteSubstitutions, updatedSubstitutionsMap)) { setAutocompleteSubstitutions(updatedSubstitutionsMap); } diff --git a/src/components/Search/SearchRouter/SearchRouter.tsx b/src/components/Search/SearchRouter/SearchRouter.tsx index 252b1c9e7e4b5..8dbde0208c475 100644 --- a/src/components/Search/SearchRouter/SearchRouter.tsx +++ b/src/components/Search/SearchRouter/SearchRouter.tsx @@ -1,5 +1,5 @@ import {findFocusedRoute, useNavigationState} from '@react-navigation/native'; -import isEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import React, {forwardRef, useCallback, useEffect, useRef, useState} from 'react'; import type {TextInputProps} from 'react-native'; import {InteractionManager, View} from 'react-native'; @@ -198,7 +198,7 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla setAutocompleteQueryValue(updatedUserQuery); const updatedSubstitutionsMap = getUpdatedSubstitutionsMap(singleLineUserQuery, autocompleteSubstitutions); - if (!isEqual(autocompleteSubstitutions, updatedSubstitutionsMap)) { + if (!deepEqual(autocompleteSubstitutions, updatedSubstitutionsMap)) { setAutocompleteSubstitutions(updatedSubstitutionsMap); } diff --git a/src/hooks/useDeepCompareRef.ts b/src/hooks/useDeepCompareRef.ts index c06978ce03a6e..46318ab886752 100644 --- a/src/hooks/useDeepCompareRef.ts +++ b/src/hooks/useDeepCompareRef.ts @@ -1,4 +1,4 @@ -import isEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import {useRef} from 'react'; /** @@ -18,7 +18,7 @@ import {useRef} from 'react'; export default function useDeepCompareRef(value: T): T | undefined { const ref = useRef(undefined); // eslint-disable-next-line react-compiler/react-compiler - if (!isEqual(value, ref.current)) { + if (!deepEqual(value, ref.current)) { // eslint-disable-next-line react-compiler/react-compiler ref.current = value; } diff --git a/src/hooks/useFetchRoute.ts b/src/hooks/useFetchRoute.ts index 3232b7e02e37d..577452b7938c9 100644 --- a/src/hooks/useFetchRoute.ts +++ b/src/hooks/useFetchRoute.ts @@ -1,4 +1,4 @@ -import isEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import {useEffect} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import * as TransactionUtils from '@libs/TransactionUtils'; @@ -24,7 +24,7 @@ export default function useFetchRoute( const isLoadingRoute = transaction?.comment?.isLoading ?? false; const validatedWaypoints = TransactionUtils.getValidWaypoints(waypoints); const previousValidatedWaypoints = usePrevious(validatedWaypoints); - const haveValidatedWaypointsChanged = !isEqual(previousValidatedWaypoints, validatedWaypoints); + const haveValidatedWaypointsChanged = !deepEqual(previousValidatedWaypoints, validatedWaypoints); const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction); const shouldFetchRoute = isDistanceRequest && (isRouteAbsentWithoutErrors || haveValidatedWaypointsChanged) && !isLoadingRoute && Object.keys(validatedWaypoints).length > 1; diff --git a/src/hooks/useSearchHighlightAndScroll.ts b/src/hooks/useSearchHighlightAndScroll.ts index e2ad8be785257..86cce7bd5e775 100644 --- a/src/hooks/useSearchHighlightAndScroll.ts +++ b/src/hooks/useSearchHighlightAndScroll.ts @@ -1,4 +1,4 @@ -import isEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import {useCallback, useEffect, useRef, useState} from 'react'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {SearchQueryJSON} from '@components/Search/types'; @@ -49,8 +49,8 @@ function useSearchHighlightAndScroll({searchResults, transactions, previousTrans if (searchTriggeredRef.current) { return; } - const hasTransactionsIDsChange = !isEqual(transactionsIDs, previousTransactionsIDs); - const hasReportActionsIDsChange = !isEqual(reportActionsIDs, previousReportActionsIDs); + const hasTransactionsIDsChange = !deepEqual(transactionsIDs, previousTransactionsIDs); + const hasReportActionsIDsChange = !deepEqual(reportActionsIDs, previousReportActionsIDs); // NOTE: This if statement should NOT assume report actions can only change // in one type, i.e.: isChat && hasReportActionsIDsChange diff --git a/src/libs/Performance.tsx b/src/libs/Performance.tsx index 868b70b9673e6..74a4fe1a863ee 100644 --- a/src/libs/Performance.tsx +++ b/src/libs/Performance.tsx @@ -1,4 +1,4 @@ -import isEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import isObject from 'lodash/isObject'; import lodashTransform from 'lodash/transform'; import React, {forwardRef, Profiler} from 'react'; @@ -17,7 +17,7 @@ import canCapturePerformanceMetrics from './Metrics'; function diffObject(object: Record, base: Record): Record { function changes(obj: Record, comparisonObject: Record): Record { return lodashTransform(obj, (result, value, key) => { - if (isEqual(value, comparisonObject[key])) { + if (deepEqual(value, comparisonObject[key])) { return; } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index cfa35f7867aed..a1498b9c1be78 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1,10 +1,10 @@ import {findFocusedRoute} from '@react-navigation/native'; import {format} from 'date-fns'; import {Str} from 'expensify-common'; +import {deepEqual} from 'fast-equals'; import lodashEscape from 'lodash/escape'; import lodashIntersection from 'lodash/intersection'; import isEmpty from 'lodash/isEmpty'; -import lodashIsEqual from 'lodash/isEqual'; import isNumber from 'lodash/isNumber'; import mapValues from 'lodash/mapValues'; import lodashMaxBy from 'lodash/maxBy'; @@ -7926,7 +7926,7 @@ function getChatByParticipants( const sortedParticipantsAccountIDs = participantAccountIDs.map(Number).sort(); // Only return the chat if it has all the participants - return lodashIsEqual(sortedNewParticipantList, sortedParticipantsAccountIDs); + return deepEqual(sortedNewParticipantList, sortedParticipantsAccountIDs); }); } diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 4c4961e8e57c3..75d81afd12116 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -1,7 +1,7 @@ import {format, isValid, parse} from 'date-fns'; +import {deepEqual} from 'fast-equals'; import lodashDeepClone from 'lodash/cloneDeep'; import lodashHas from 'lodash/has'; -import lodashIsEqual from 'lodash/isEqual'; import lodashSet from 'lodash/set'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; @@ -1347,7 +1347,7 @@ function compareDuplicateTransactionFields( // Helper function to check if all comments are equal function areAllCommentsEqual(items: Array>, firstTransaction: OnyxEntry) { - return items.every((item) => lodashIsEqual(getDescription(item), getDescription(firstTransaction))); + return items.every((item) => deepEqual(getDescription(item), getDescription(firstTransaction))); } // Helper function to check if all fields are equal for a given key diff --git a/src/libs/actions/PersistedRequests.ts b/src/libs/actions/PersistedRequests.ts index ad2e3285ba7cd..2ed61cf7318a2 100644 --- a/src/libs/actions/PersistedRequests.ts +++ b/src/libs/actions/PersistedRequests.ts @@ -1,4 +1,4 @@ -import isEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import Onyx from 'react-native-onyx'; import Log from '@libs/Log'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -18,7 +18,7 @@ Onyx.connect({ // We try to remove the next request from the persistedRequests if it is the same as ongoingRequest // so we don't process it twice. - if (isEqual(nextRequestToProcess, ongoingRequest)) { + if (deepEqual(nextRequestToProcess, ongoingRequest)) { persistedRequests = persistedRequests.slice(1); } } @@ -61,7 +61,7 @@ function endRequestAndRemoveFromQueue(requestToRemove: Request) { * If we were to remove all matching requests, we can end up with a final state that is different than what the user intended. */ const requests = [...persistedRequests]; - const index = requests.findIndex((persistedRequest) => isEqual(persistedRequest, requestToRemove)); + const index = requests.findIndex((persistedRequest) => deepEqual(persistedRequest, requestToRemove)); if (index !== -1) { requests.splice(index, 1); diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index b2d8f6a51165b..ec576a92de309 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -1,7 +1,7 @@ import {getUnixTime} from 'date-fns'; +import {deepEqual} from 'fast-equals'; import lodashClone from 'lodash/clone'; import lodashHas from 'lodash/has'; -import isEqual from 'lodash/isEqual'; import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; @@ -124,7 +124,7 @@ function saveWaypoint(transactionID: string, index: string, waypoint: RecentWayp // If current location is used, we would want to avoid saving it as a recent waypoint. This prevents the 'Your Location' // text from showing up in the address search suggestions - if (isEqual(waypoint?.address, CONST.YOUR_LOCATION_TEXT)) { + if (deepEqual(waypoint?.address, CONST.YOUR_LOCATION_TEXT)) { return; } const recentWaypointAlreadyExists = recentWaypoints.find((recentWaypoint) => recentWaypoint?.address === waypoint?.address); diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 2242e97758c1e..72a7f4cf16bd4 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -1,6 +1,6 @@ import {PortalHost} from '@gorhom/portal'; import {useIsFocused} from '@react-navigation/native'; -import lodashIsEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import type {FlatList, ViewStyle} from 'react-native'; import {DeviceEventEmitter, InteractionManager, View} from 'react-native'; @@ -832,4 +832,4 @@ function ReportScreen({route, navigation}: ReportScreenProps) { } ReportScreen.displayName = 'ReportScreen'; -export default memo(ReportScreen, (prevProps, nextProps) => lodashIsEqual(prevProps.route, nextProps.route)); +export default memo(ReportScreen, (prevProps, nextProps) => deepEqual(prevProps.route, nextProps.route)); diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index 2a236649aa07e..3f42963d3fbbd 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -1,4 +1,4 @@ -import lodashIsEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import type {MutableRefObject, RefObject} from 'react'; import React, {memo, useMemo, useRef, useState} from 'react'; import {InteractionManager, View} from 'react-native'; @@ -391,6 +391,6 @@ function BaseReportActionContextMenu({ ); } -export default memo(BaseReportActionContextMenu, lodashIsEqual); +export default memo(BaseReportActionContextMenu, deepEqual); export type {BaseReportActionContextMenuProps}; diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index ceeeff671d72e..de4b36ac4f0d1 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -1,4 +1,4 @@ -import lodashIsEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import mapValues from 'lodash/mapValues'; import React, {memo, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; import type {GestureResponderEvent, TextInput} from 'react-native'; @@ -515,7 +515,7 @@ function PureReportActionItem({ } const urls = extractLinksFromMessageHtml(action); - if (lodashIsEqual(downloadedPreviews.current, urls) || action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { + if (deepEqual(downloadedPreviews.current, urls) || action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { return; } @@ -1489,10 +1489,10 @@ export default memo(PureReportActionItem, (prevProps, nextProps) => { prevProps.displayAsGroup === nextProps.displayAsGroup && prevProps.isMostRecentIOUReportAction === nextProps.isMostRecentIOUReportAction && prevProps.shouldDisplayNewMarker === nextProps.shouldDisplayNewMarker && - lodashIsEqual(prevProps.action, nextProps.action) && - lodashIsEqual(prevProps.report?.pendingFields, nextProps.report?.pendingFields) && - lodashIsEqual(prevProps.report?.isDeletedParentAction, nextProps.report?.isDeletedParentAction) && - lodashIsEqual(prevProps.report?.errorFields, nextProps.report?.errorFields) && + deepEqual(prevProps.action, nextProps.action) && + deepEqual(prevProps.report?.pendingFields, nextProps.report?.pendingFields) && + deepEqual(prevProps.report?.isDeletedParentAction, nextProps.report?.isDeletedParentAction) && + deepEqual(prevProps.report?.errorFields, nextProps.report?.errorFields) && prevProps.report?.statusNum === nextProps.report?.statusNum && prevProps.report?.stateNum === nextProps.report?.stateNum && prevProps.report?.parentReportID === nextProps.report?.parentReportID && @@ -1509,24 +1509,24 @@ export default memo(PureReportActionItem, (prevProps, nextProps) => { prevProps.report?.nonReimbursableTotal === nextProps.report?.nonReimbursableTotal && prevProps.report?.policyAvatar === nextProps.report?.policyAvatar && prevProps.linkedReportActionID === nextProps.linkedReportActionID && - lodashIsEqual(prevProps.report?.fieldList, nextProps.report?.fieldList) && - lodashIsEqual(prevProps.transactionThreadReport, nextProps.transactionThreadReport) && - lodashIsEqual(prevProps.reportActions, nextProps.reportActions) && - lodashIsEqual(prevParentReportAction, nextParentReportAction) && + deepEqual(prevProps.report?.fieldList, nextProps.report?.fieldList) && + deepEqual(prevProps.transactionThreadReport, nextProps.transactionThreadReport) && + deepEqual(prevProps.reportActions, nextProps.reportActions) && + deepEqual(prevParentReportAction, nextParentReportAction) && prevProps.draftMessage === nextProps.draftMessage && prevProps.iouReport?.reportID === nextProps.iouReport?.reportID && - lodashIsEqual(prevProps.emojiReactions, nextProps.emojiReactions) && - lodashIsEqual(prevProps.linkedTransactionRouteError, nextProps.linkedTransactionRouteError) && - lodashIsEqual(prevProps.reportNameValuePairs, nextProps.reportNameValuePairs) && + deepEqual(prevProps.emojiReactions, nextProps.emojiReactions) && + deepEqual(prevProps.linkedTransactionRouteError, nextProps.linkedTransactionRouteError) && + deepEqual(prevProps.reportNameValuePairs, nextProps.reportNameValuePairs) && prevProps.isUserValidated === nextProps.isUserValidated && prevProps.parentReport?.reportID === nextProps.parentReport?.reportID && - lodashIsEqual(prevProps.personalDetails, nextProps.personalDetails) && - lodashIsEqual(prevProps.blockedFromConcierge, nextProps.blockedFromConcierge) && + deepEqual(prevProps.personalDetails, nextProps.personalDetails) && + deepEqual(prevProps.blockedFromConcierge, nextProps.blockedFromConcierge) && prevProps.originalReportID === nextProps.originalReportID && prevProps.isArchivedRoom === nextProps.isArchivedRoom && prevProps.isChronosReport === nextProps.isChronosReport && prevProps.isClosedExpenseReportWithNoExpenses === nextProps.isClosedExpenseReportWithNoExpenses && - lodashIsEqual(prevProps.missingPaymentMethod, nextProps.missingPaymentMethod) && + deepEqual(prevProps.missingPaymentMethod, nextProps.missingPaymentMethod) && prevProps.reimbursementDeQueuedOrCanceledActionMessage === nextProps.reimbursementDeQueuedOrCanceledActionMessage && prevProps.modifiedExpenseMessage === nextProps.modifiedExpenseMessage && prevProps.userBillingFundID === nextProps.userBillingFundID && diff --git a/src/pages/home/report/ReportActionItemContentCreated.tsx b/src/pages/home/report/ReportActionItemContentCreated.tsx index a5229be89a4b1..32cb4c2a7174f 100644 --- a/src/pages/home/report/ReportActionItemContentCreated.tsx +++ b/src/pages/home/report/ReportActionItemContentCreated.tsx @@ -1,4 +1,4 @@ -import lodashIsEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import React, {memo, useMemo} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; @@ -196,8 +196,8 @@ ReportActionItemContentCreated.displayName = 'ReportActionItemContentCreated'; export default memo( ReportActionItemContentCreated, (prevProps, nextProps) => - lodashIsEqual(prevProps.contextValue, nextProps.contextValue) && - lodashIsEqual(prevProps.parentReportAction, nextProps.parentReportAction) && + deepEqual(prevProps.contextValue, nextProps.contextValue) && + deepEqual(prevProps.parentReportAction, nextProps.parentReportAction) && prevProps.transactionID === nextProps.transactionID && prevProps.draftMessage === nextProps.draftMessage && prevProps.shouldHideThreadDividerLine === nextProps.shouldHideThreadDividerLine, diff --git a/src/pages/home/report/ReportFooter.tsx b/src/pages/home/report/ReportFooter.tsx index f8503c3396588..40f32aa92eb93 100644 --- a/src/pages/home/report/ReportFooter.tsx +++ b/src/pages/home/report/ReportFooter.tsx @@ -1,5 +1,5 @@ import {Str} from 'expensify-common'; -import lodashIsEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import React, {memo, useCallback, useEffect, useState} from 'react'; import {Keyboard, View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; @@ -238,12 +238,12 @@ ReportFooter.displayName = 'ReportFooter'; export default memo( ReportFooter, (prevProps, nextProps) => - lodashIsEqual(prevProps.report, nextProps.report) && + deepEqual(prevProps.report, nextProps.report) && prevProps.pendingAction === nextProps.pendingAction && prevProps.isComposerFullSize === nextProps.isComposerFullSize && prevProps.lastReportAction === nextProps.lastReportAction && prevProps.isReportReadyForDisplay === nextProps.isReportReadyForDisplay && - lodashIsEqual(prevProps.reportMetadata, nextProps.reportMetadata) && - lodashIsEqual(prevProps.policy?.employeeList, nextProps.policy?.employeeList) && - lodashIsEqual(prevProps.policy?.role, nextProps.policy?.role), + deepEqual(prevProps.reportMetadata, nextProps.reportMetadata) && + deepEqual(prevProps.policy?.employeeList, nextProps.policy?.employeeList) && + deepEqual(prevProps.policy?.role, nextProps.policy?.role), ); diff --git a/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx b/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx index 3f3fef17d3de5..726b4b1e89a59 100644 --- a/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx +++ b/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx @@ -1,4 +1,4 @@ -import lodashIsEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import lodashReject from 'lodash/reject'; import React, {memo, useCallback, useEffect, useMemo} from 'react'; import type {GestureResponderEvent} from 'react-native'; @@ -314,4 +314,4 @@ function MoneyRequestAttendeeSelector({attendees = [], onFinish, onAttendeesAdde MoneyRequestAttendeeSelector.displayName = 'MoneyRequestAttendeeSelector'; -export default memo(MoneyRequestAttendeeSelector, (prevProps, nextProps) => lodashIsEqual(prevProps.attendees, nextProps.attendees) && prevProps.iouType === nextProps.iouType); +export default memo(MoneyRequestAttendeeSelector, (prevProps, nextProps) => deepEqual(prevProps.attendees, nextProps.attendees) && prevProps.iouType === nextProps.iouType); diff --git a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx index d163a82df8204..3ffbd01ec9fbb 100644 --- a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx +++ b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx @@ -1,4 +1,4 @@ -import lodashIsEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import lodashPick from 'lodash/pick'; import lodashReject from 'lodash/reject'; import React, {memo, useCallback, useEffect, useMemo} from 'react'; @@ -481,4 +481,4 @@ function MoneyRequestParticipantsSelector({ MoneyRequestParticipantsSelector.displayName = 'MoneyTemporaryForRefactorRequestParticipantsSelector'; -export default memo(MoneyRequestParticipantsSelector, (prevProps, nextProps) => lodashIsEqual(prevProps.participants, nextProps.participants) && prevProps.iouType === nextProps.iouType); +export default memo(MoneyRequestParticipantsSelector, (prevProps, nextProps) => deepEqual(prevProps.participants, nextProps.participants) && prevProps.iouType === nextProps.iouType); diff --git a/src/pages/iou/request/step/IOURequestStepAttendees.tsx b/src/pages/iou/request/step/IOURequestStepAttendees.tsx index 6e9f674d7a790..8a68375bd6a94 100644 --- a/src/pages/iou/request/step/IOURequestStepAttendees.tsx +++ b/src/pages/iou/request/step/IOURequestStepAttendees.tsx @@ -1,4 +1,4 @@ -import lodashIsEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import React, {useCallback, useState} from 'react'; import {useOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; @@ -50,7 +50,7 @@ function IOURequestStepAttendees({ if (attendees.length <= 0) { return; } - if (!lodashIsEqual(previousAttendees, attendees)) { + if (!deepEqual(previousAttendees, attendees)) { setMoneyRequestAttendees(transactionID, attendees, !isEditing); if (isEditing) { updateMoneyRequestAttendees(transactionID, reportID, attendees, policy, policyTags, policyCategories, transactionViolations ?? undefined); diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 3c39fee662635..881077af41b44 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -1,5 +1,5 @@ +import {deepEqual} from 'fast-equals'; import isEmpty from 'lodash/isEmpty'; -import isEqual from 'lodash/isEqual'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; // eslint-disable-next-line no-restricted-imports @@ -429,7 +429,7 @@ function IOURequestStepDistance({ const updateWaypoints = useCallback( ({data}: DataParams) => { - if (isEqual(waypointsList, data)) { + if (deepEqual(waypointsList, data)) { return; } @@ -469,8 +469,8 @@ function IOURequestStepDistance({ const oldWaypoints = transactionBackup?.comment?.waypoints ?? {}; const oldAddresses = Object.fromEntries(Object.entries(oldWaypoints).map(([key, waypoint]) => [key, 'address' in waypoint ? waypoint.address : {}])); const addresses = Object.fromEntries(Object.entries(waypoints).map(([key, waypoint]) => [key, 'address' in waypoint ? waypoint.address : {}])); - const hasRouteChanged = !isEqual(transactionBackup?.routes, transaction?.routes); - if (isEqual(oldAddresses, addresses)) { + const hasRouteChanged = !deepEqual(transactionBackup?.routes, transaction?.routes); + if (deepEqual(oldAddresses, addresses)) { navigateBack(); return; } diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx index 30dbe12f0ebbd..f9278ccf6f750 100644 --- a/src/pages/workspace/WorkspaceMembersPage.tsx +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -1,5 +1,5 @@ import {useIsFocused} from '@react-navigation/native'; -import lodashIsEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import type {TextInput} from 'react-native'; import {InteractionManager, View} from 'react-native'; @@ -227,7 +227,7 @@ function WorkspaceMembersPage({personalDetails, route, policy, currentUserPerson }, [preferredLocale, validateSelection]); useEffect(() => { - if (removeMembersConfirmModalVisible && !lodashIsEqual(accountIDs, prevAccountIDs)) { + if (removeMembersConfirmModalVisible && !deepEqual(accountIDs, prevAccountIDs)) { setRemoveMembersConfirmModalVisible(false); } setSelectedEmployees((prevSelectedEmployees) => { diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index b41e947f5ded0..a83302944687e 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -1,6 +1,6 @@ import {renderHook} from '@testing-library/react-native'; import {format} from 'date-fns'; -import isEqual from 'lodash/isEqual'; +import {deepEqual} from 'fast-equals'; import type {OnyxCollection, OnyxEntry, OnyxInputValue} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import useReportWithTransactionsAndViolations from '@hooks/useReportWithTransactionsAndViolations'; @@ -1861,7 +1861,8 @@ describe('actions/IOU', () => { // 5. The chat report with Rory + Vit (new) vitChatReport = Object.values(allReports ?? {}).find( (report) => - report?.type === CONST.REPORT.TYPE.CHAT && isEqual(report.participants, {[RORY_ACCOUNT_ID]: RORY_PARTICIPANT, [VIT_ACCOUNT_ID]: VIT_PARTICIPANT}), + report?.type === CONST.REPORT.TYPE.CHAT && + deepEqual(report.participants, {[RORY_ACCOUNT_ID]: RORY_PARTICIPANT, [VIT_ACCOUNT_ID]: VIT_PARTICIPANT}), ); expect(isEmptyObject(vitChatReport)).toBe(false); expect(vitChatReport?.pendingFields).toStrictEqual({createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}); @@ -1875,7 +1876,7 @@ describe('actions/IOU', () => { groupChat = Object.values(allReports ?? {}).find( (report) => report?.type === CONST.REPORT.TYPE.CHAT && - isEqual(report.participants, { + deepEqual(report.participants, { [CARLOS_ACCOUNT_ID]: CARLOS_PARTICIPANT, [JULES_ACCOUNT_ID]: JULES_PARTICIPANT, [VIT_ACCOUNT_ID]: VIT_PARTICIPANT, From 2057e9df97008276667297ac4051ef3da27ecf5e Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Fri, 16 May 2025 22:34:11 +0530 Subject: [PATCH 2/9] minor fix. Signed-off-by: krishna2323 --- .eslintrc.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 0b33de33fd164..7fb956e5c1bf6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -88,7 +88,12 @@ const restrictedImportPaths = [ }, { name: 'lodash', - importNames: ['memoize', 'isEqual'], + importNames: ['isEqual'], + message: "Please use 'deepEqual' from 'fast-equals' instead.", + }, + { + name: 'lodash', + importNames: ['memoize'], message: "Please use '@src/libs/memoize' instead.", }, { From 7dd230c854eea5fa1057a3332b3f4bbb6a2a46d0 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Fri, 16 May 2025 22:35:14 +0530 Subject: [PATCH 3/9] minor update. Signed-off-by: krishna2323 --- .eslintrc.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 7fb956e5c1bf6..cb5b65818c58e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -82,6 +82,11 @@ const restrictedImportPaths = [ name: 'lodash/memoize', message: "Please use '@src/libs/memoize' instead.", }, + { + name: 'lodash', + importNames: ['memoize'], + message: "Please use '@src/libs/memoize' instead.", + }, { name: 'lodash/isEqual', message: "Please use 'deepEqual' from 'fast-equals' instead.", @@ -91,11 +96,6 @@ const restrictedImportPaths = [ importNames: ['isEqual'], message: "Please use 'deepEqual' from 'fast-equals' instead.", }, - { - name: 'lodash', - importNames: ['memoize'], - message: "Please use '@src/libs/memoize' instead.", - }, { name: 'react-native-animatable', message: "Please use 'react-native-reanimated' instead.", From 06cb7a1a4b90d22be44449ac799ea6ed08686e64 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Fri, 16 May 2025 22:48:07 +0530 Subject: [PATCH 4/9] fix: ESLint. Signed-off-by: krishna2323 --- src/components/OptionRow.tsx | 4 ++-- src/hooks/useFetchRoute.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/OptionRow.tsx b/src/components/OptionRow.tsx index ee7b110d06651..7d27e045638cd 100644 --- a/src/components/OptionRow.tsx +++ b/src/components/OptionRow.tsx @@ -149,7 +149,7 @@ function OptionRow({ const firstIcon = option?.icons?.at(0); // We only create tooltips for the first 10 users or so since some reports have hundreds of users, causing performance to degrade. - const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips((option.participantsList ?? (option.accountID ? [option] : [])).slice(0, 10), shouldUseShortFormInTooltip); + const displayNamesWithTooltips = getDisplayNamesWithTooltips((option.participantsList ?? (option.accountID ? [option] : [])).slice(0, 10), shouldUseShortFormInTooltip); let subscriptColor = theme.appBG; if (optionIsFocused) { subscriptColor = focusedBackgroundColor; @@ -221,7 +221,7 @@ function OptionRow({ icons={option.icons} size={CONST.AVATAR_SIZE.DEFAULT} secondAvatarStyle={[StyleUtils.getBackgroundAndBorderStyle(hovered && !optionIsFocused ? hoveredBackgroundColor : subscriptColor)]} - shouldShowTooltip={showTitleTooltip && OptionsListUtils.shouldOptionShowTooltip(option)} + shouldShowTooltip={showTitleTooltip && shouldOptionShowTooltip(option)} /> ))} diff --git a/src/hooks/useFetchRoute.ts b/src/hooks/useFetchRoute.ts index 577452b7938c9..90cf2350ae418 100644 --- a/src/hooks/useFetchRoute.ts +++ b/src/hooks/useFetchRoute.ts @@ -1,7 +1,7 @@ import {deepEqual} from 'fast-equals'; import {useEffect} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import {getValidWaypoints, hasRoute as hasRouteTransactionUtils, isDistanceRequest as isDistanceRequestTransactionUtils} from '@libs/TransactionUtils'; import * as TransactionAction from '@userActions/Transaction'; import type {IOUAction} from '@src/CONST'; import CONST from '@src/CONST'; @@ -19,13 +19,13 @@ export default function useFetchRoute( ) { const {isOffline} = useNetwork(); const hasRouteError = !!transaction?.errorFields?.route; - const hasRoute = TransactionUtils.hasRoute(transaction); + const hasRoute = hasRouteTransactionUtils(transaction); const isRouteAbsentWithoutErrors = !hasRoute && !hasRouteError; const isLoadingRoute = transaction?.comment?.isLoading ?? false; - const validatedWaypoints = TransactionUtils.getValidWaypoints(waypoints); + const validatedWaypoints = getValidWaypoints(waypoints); const previousValidatedWaypoints = usePrevious(validatedWaypoints); const haveValidatedWaypointsChanged = !deepEqual(previousValidatedWaypoints, validatedWaypoints); - const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction); + const isDistanceRequest = isDistanceRequestTransactionUtils(transaction); const shouldFetchRoute = isDistanceRequest && (isRouteAbsentWithoutErrors || haveValidatedWaypointsChanged) && !isLoadingRoute && Object.keys(validatedWaypoints).length > 1; useEffect(() => { From 133f22ebf6e983cd5eb7e619cb913225de5ba6e5 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Sat, 17 May 2025 19:44:08 +0530 Subject: [PATCH 5/9] fix: ESLint. Signed-off-by: krishna2323 --- src/components/OptionRow.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/OptionRow.tsx b/src/components/OptionRow.tsx index 7d27e045638cd..8b38a1b2fa0ec 100644 --- a/src/components/OptionRow.tsx +++ b/src/components/OptionRow.tsx @@ -6,8 +6,8 @@ import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as ReportUtils from '@libs/ReportUtils'; +import {shouldOptionShowTooltip} from '@libs/OptionsListUtils'; +import {getDisplayNamesWithTooltips} from '@libs/ReportUtils'; import type {OptionData} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import Button from './Button'; From 29a133fdf561fd938cfee05849c687f33f0b5710 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Sat, 17 May 2025 20:54:41 +0530 Subject: [PATCH 6/9] fix: import ESLint warning. Signed-off-by: krishna2323 --- src/hooks/useFetchRoute.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useFetchRoute.ts b/src/hooks/useFetchRoute.ts index 90cf2350ae418..e4d2d7a42fdeb 100644 --- a/src/hooks/useFetchRoute.ts +++ b/src/hooks/useFetchRoute.ts @@ -1,8 +1,8 @@ import {deepEqual} from 'fast-equals'; import {useEffect} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; +import {getRoute} from '@libs/actions/Transaction'; import {getValidWaypoints, hasRoute as hasRouteTransactionUtils, isDistanceRequest as isDistanceRequestTransactionUtils} from '@libs/TransactionUtils'; -import * as TransactionAction from '@userActions/Transaction'; import type {IOUAction} from '@src/CONST'; import CONST from '@src/CONST'; import type {Transaction} from '@src/types/onyx'; @@ -33,7 +33,7 @@ export default function useFetchRoute( return; } - TransactionAction.getRoute(transaction.transactionID, validatedWaypoints, transactionState); + getRoute(transaction.transactionID, validatedWaypoints, transactionState); }, [shouldFetchRoute, transaction?.transactionID, validatedWaypoints, isOffline, action, transactionState]); return {shouldFetchRoute, validatedWaypoints}; From b7541637771a9fa263665807da6e8427b97afcee Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Sat, 24 May 2025 01:32:52 +0530 Subject: [PATCH 7/9] use circularDeepEqual in PopoverWithMeasuredContent. Signed-off-by: krishna2323 --- package-lock.json | 17 ++++++++++++++--- package.json | 2 +- src/components/PopoverWithMeasuredContent.tsx | 4 ++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1021f4ac01dec..e282e289c7430 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,7 +65,7 @@ "expo-font": "^13.0.4", "expo-image": "^2.0.4", "expo-image-manipulator": "^13.0.6", - "fast-equals": "^4.0.3", + "fast-equals": "^5.2.2", "focus-trap-react": "^11.0.3", "howler": "^2.2.4", "htmlparser2": "^7.2.0", @@ -23681,8 +23681,13 @@ "license": "Apache-2.0" }, "node_modules/fast-equals": { - "version": "4.0.3", - "license": "MIT" + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz", + "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } }, "node_modules/fast-glob": { "version": "3.3.3", @@ -33963,6 +33968,12 @@ } } }, + "node_modules/react-native-onyx/node_modules/fast-equals": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz", + "integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==", + "license": "MIT" + }, "node_modules/react-native-pager-view": { "version": "6.5.3", "resolved": "https://registry.npmjs.org/react-native-pager-view/-/react-native-pager-view-6.5.3.tgz", diff --git a/package.json b/package.json index a1a66e955ea02..9db40ff341617 100644 --- a/package.json +++ b/package.json @@ -132,7 +132,7 @@ "expo-font": "^13.0.4", "expo-image": "^2.0.4", "expo-image-manipulator": "^13.0.6", - "fast-equals": "^4.0.3", + "fast-equals": "^5.2.2", "focus-trap-react": "^11.0.3", "howler": "^2.2.4", "htmlparser2": "^7.2.0", diff --git a/src/components/PopoverWithMeasuredContent.tsx b/src/components/PopoverWithMeasuredContent.tsx index 8cc156317fe8d..9d3e8fbcc377e 100644 --- a/src/components/PopoverWithMeasuredContent.tsx +++ b/src/components/PopoverWithMeasuredContent.tsx @@ -1,4 +1,4 @@ -import {deepEqual} from 'fast-equals'; +import {circularDeepEqual} from 'fast-equals'; import React, {useMemo, useState} from 'react'; import type {LayoutChangeEvent} from 'react-native'; import {View} from 'react-native'; @@ -195,7 +195,7 @@ export default React.memo(PopoverWithMeasuredContent, (prevProps, nextProps) => if (prevProps.isVisible === nextProps.isVisible && nextProps.isVisible === false) { return true; } - return deepEqual(prevProps, nextProps); + return circularDeepEqual(prevProps, nextProps); }); export type {PopoverWithMeasuredContentProps}; From 86dbb5a7372217fca264422d039f96d4ed2cd3ec Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Mon, 9 Jun 2025 09:01:10 +0530 Subject: [PATCH 8/9] fix ESLint. Signed-off-by: krishna2323 --- src/pages/iou/request/step/IOURequestStepAttendees.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepAttendees.tsx b/src/pages/iou/request/step/IOURequestStepAttendees.tsx index 8a68375bd6a94..f409ccff0b692 100644 --- a/src/pages/iou/request/step/IOURequestStepAttendees.tsx +++ b/src/pages/iou/request/step/IOURequestStepAttendees.tsx @@ -40,7 +40,7 @@ function IOURequestStepAttendees({ policyCategories, }: IOURequestStepAttendeesProps) { const isEditing = action === CONST.IOU.ACTION.EDIT; - const [transaction] = useOnyx(`${isEditing ? ONYXKEYS.COLLECTION.TRANSACTION : ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID || CONST.DEFAULT_NUMBER_ID}`); + const [transaction] = useOnyx(`${isEditing ? ONYXKEYS.COLLECTION.TRANSACTION : ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID || CONST.DEFAULT_NUMBER_ID}`, {canBeMissing: true}); const [attendees, setAttendees] = useState(() => getAttendees(transaction)); const previousAttendees = usePrevious(attendees); const {translate} = useLocalize(); From 978d0611f79c836dcf4e6ad9af41f33fd9dc2353 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Fri, 13 Jun 2025 20:42:24 +0530 Subject: [PATCH 9/9] fix ESLint. Signed-off-by: krishna2323 --- src/components/PopoverWithMeasuredContent.tsx | 6 +++--- src/pages/iou/request/step/IOURequestStepAttendees.tsx | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/PopoverWithMeasuredContent.tsx b/src/components/PopoverWithMeasuredContent.tsx index 5e615766c05df..a811d1f6f378f 100644 --- a/src/components/PopoverWithMeasuredContent.tsx +++ b/src/components/PopoverWithMeasuredContent.tsx @@ -1,4 +1,4 @@ -import {circularDeepEqual} from 'fast-equals'; +import {circularDeepEqual, deepEqual} from 'fast-equals'; import React, {useContext, useMemo, useState} from 'react'; import type {LayoutChangeEvent} from 'react-native'; import {View} from 'react-native'; @@ -90,8 +90,8 @@ function PopoverWithMeasuredContent({ if (!prevIsVisible && isVisible && isContentMeasured && !shouldSkipRemeasurement) { // Check if anything significant changed that would require re-measurement - const hasAnchorPositionChanged = !isEqual(prevAnchorPosition, anchorPosition); - const hasWindowSizeChanged = !isEqual(prevWindowDimensions, {windowWidth, windowHeight}); + const hasAnchorPositionChanged = !deepEqual(prevAnchorPosition, anchorPosition); + const hasWindowSizeChanged = !deepEqual(prevWindowDimensions, {windowWidth, windowHeight}); const hasStaticDimensions = popoverDimensions.width > 0 && popoverDimensions.height > 0; // Only reset if: diff --git a/src/pages/iou/request/step/IOURequestStepAttendees.tsx b/src/pages/iou/request/step/IOURequestStepAttendees.tsx index f409ccff0b692..abc1bdf95e068 100644 --- a/src/pages/iou/request/step/IOURequestStepAttendees.tsx +++ b/src/pages/iou/request/step/IOURequestStepAttendees.tsx @@ -40,6 +40,7 @@ function IOURequestStepAttendees({ policyCategories, }: IOURequestStepAttendeesProps) { const isEditing = action === CONST.IOU.ACTION.EDIT; + // eslint-disable-next-line rulesdir/no-default-id-values const [transaction] = useOnyx(`${isEditing ? ONYXKEYS.COLLECTION.TRANSACTION : ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID || CONST.DEFAULT_NUMBER_ID}`, {canBeMissing: true}); const [attendees, setAttendees] = useState(() => getAttendees(transaction)); const previousAttendees = usePrevious(attendees);