From 452dd64ef4d3aca86037d4f91ed71dd116fc0eee Mon Sep 17 00:00:00 2001 From: mkzie2 Date: Thu, 18 Sep 2025 21:41:54 +0700 Subject: [PATCH 1/3] remove inline selector for transaction key --- .../TransactionPreview/index.tsx | 10 +--- src/hooks/useReportTransactions.ts | 11 ++++- src/hooks/useTransactionsByID.ts | 20 ++++++++ src/hooks/useTripTransactions.ts | 22 ++++++--- src/pages/AddUnreportedExpense.tsx | 47 ++++++++++--------- src/pages/Search/EmptySearchView.tsx | 7 ++- .../TransactionDuplicate/Confirmation.tsx | 21 +++++---- .../DuplicateTransactionItem.tsx | 4 +- .../TransactionDuplicate/ReviewBillable.tsx | 11 ++--- .../TransactionDuplicate/ReviewCategory.tsx | 10 +--- .../ReviewDescription.tsx | 10 +--- .../TransactionDuplicate/ReviewMerchant.tsx | 10 +--- .../ReviewReimbursable.tsx | 10 +--- src/pages/TransactionDuplicate/ReviewTag.tsx | 11 +---- .../TransactionDuplicate/ReviewTaxCode.tsx | 10 +--- 15 files changed, 105 insertions(+), 109 deletions(-) create mode 100644 src/hooks/useTransactionsByID.ts diff --git a/src/components/ReportActionItem/TransactionPreview/index.tsx b/src/components/ReportActionItem/TransactionPreview/index.tsx index 8aeae248a003..b9cfd50793ac 100644 --- a/src/components/ReportActionItem/TransactionPreview/index.tsx +++ b/src/components/ReportActionItem/TransactionPreview/index.tsx @@ -21,6 +21,7 @@ import type {TransactionDuplicateNavigatorParamList} from '@navigation/types'; import {clearWalletTermsError} from '@userActions/PaymentMethods'; import {clearIOUError} from '@userActions/Report'; import CONST from '@src/CONST'; +import useTransactionsByID from '@src/hooks/useTransactionsByID'; import ONYXKEYS from '@src/ONYXKEYS'; import SCREENS from '@src/SCREENS'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -57,14 +58,7 @@ function TransactionPreview(props: TransactionPreviewProps) { // Get transaction violations for given transaction id from onyx, find duplicated transactions violations and get duplicates const allDuplicateIDs = useMemo(() => violations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], [violations]); - const [allDuplicates] = useOnyx( - ONYXKEYS.COLLECTION.TRANSACTION, - { - selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), - canBeMissing: true, - }, - [allDuplicateIDs], - ); + const [allDuplicates] = useTransactionsByID(allDuplicateIDs); const duplicates = useMemo(() => removeSettledAndApprovedTransactions(allDuplicates ?? []), [allDuplicates]); const sessionAccountID = session?.accountID; const areThereDuplicates = allDuplicateIDs.length > 0 && duplicates.length > 0 && allDuplicateIDs.length === duplicates.length; diff --git a/src/hooks/useReportTransactions.ts b/src/hooks/useReportTransactions.ts index ccc18f5cba6c..f7a951d5d4a1 100644 --- a/src/hooks/useReportTransactions.ts +++ b/src/hooks/useReportTransactions.ts @@ -1,3 +1,5 @@ +import {useCallback} from 'react'; +import type {OnyxCollection} from 'react-native-onyx'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Transaction} from '@src/types/onyx'; import getEmptyArray from '@src/types/utils/getEmptyArray'; @@ -7,14 +9,19 @@ import useOnyx from './useOnyx'; * Hook to get all transactions for a specific report */ function useReportTransactions(reportID: string | undefined): Transaction[] { - const [reportTransactions = getEmptyArray()] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { - selector: (transactions) => { + const reportTransactionsSelector = useCallback( + (transactions: OnyxCollection) => { if (!transactions || !reportID) { return []; } return Object.values(transactions).filter((transaction): transaction is Transaction => !!transaction && transaction.reportID === reportID); }, + [reportID], + ); + + const [reportTransactions = getEmptyArray()] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { + selector: reportTransactionsSelector, canBeMissing: true, }); diff --git a/src/hooks/useTransactionsByID.ts b/src/hooks/useTransactionsByID.ts new file mode 100644 index 000000000000..629bba47ccdf --- /dev/null +++ b/src/hooks/useTransactionsByID.ts @@ -0,0 +1,20 @@ +import {useCallback} from 'react'; +import type {OnyxCollection} from 'react-native-onyx'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Transaction} from '@src/types/onyx'; +import useOnyx from './useOnyx'; + +function useTransactionsByID(transactionIDs: string[]) { + const transactionsSelector = useCallback( + (transactions: OnyxCollection) => transactionIDs.map((id) => transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), + [transactionIDs], + ); + + const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { + selector: transactionsSelector, + }); + + return [transactions]; +} + +export default useTransactionsByID; diff --git a/src/hooks/useTripTransactions.ts b/src/hooks/useTripTransactions.ts index bf15ec0876fe..23794f04c32c 100644 --- a/src/hooks/useTripTransactions.ts +++ b/src/hooks/useTripTransactions.ts @@ -1,3 +1,5 @@ +import {useCallback} from 'react'; +import type {OnyxCollection} from 'react-native-onyx'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Transaction} from '@src/types/onyx'; import getEmptyArray from '@src/types/utils/getEmptyArray'; @@ -20,16 +22,22 @@ function useTripTransactions(reportID: string | undefined): Transaction[] { .filter((report) => report && report.chatReportID === reportID) .map((report) => report?.reportID), }); + + const tripTransactionsSelector = useCallback( + (transactions: OnyxCollection) => { + if (!tripTransactionReportIDs.length) { + return []; + } + + return Object.values(transactions ?? {}).filter((transaction): transaction is Transaction => !!transaction && tripTransactionReportIDs.includes(transaction.reportID)); + }, + [tripTransactionReportIDs], + ); + const [tripTransactions = getEmptyArray()] = useOnyx( ONYXKEYS.COLLECTION.TRANSACTION, { - selector: (transactions) => { - if (!tripTransactionReportIDs.length) { - return []; - } - - return Object.values(transactions ?? {}).filter((transaction): transaction is Transaction => !!transaction && tripTransactionReportIDs.includes(transaction.reportID)); - }, + selector: tripTransactionsSelector, }, [tripTransactionReportIDs], ); diff --git a/src/pages/AddUnreportedExpense.tsx b/src/pages/AddUnreportedExpense.tsx index 20cf2cb16715..f70f5f379a38 100644 --- a/src/pages/AddUnreportedExpense.tsx +++ b/src/pages/AddUnreportedExpense.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useMemo, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {InteractionManager} from 'react-native'; import type {OnyxCollection} from 'react-native-onyx'; import EmptyStateComponent from '@components/EmptyStateComponent'; @@ -56,30 +56,33 @@ function AddUnreportedExpense({route}: AddUnreportedExpensePageType) { const session = useSession(); const shouldShowUnreportedTransactionsSkeletons = isLoadingUnreportedTransactions && hasMoreUnreportedTransactionsResults && !isOffline; - function getUnreportedTransactions(transactions: OnyxCollection) { - if (!transactions) { - return []; - } - return Object.values(transactions || {}).filter((item) => { - const isUnreported = item?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID || item?.reportID === ''; - if (!isUnreported) { - return false; - } - - if (isPerDiemRequest(item)) { - // Only show per diem expenses if the target workspace has per diem enabled and the per diem expense was created in the same workspace - const workspacePerDiemUnit = getPerDiemCustomUnit(policy); - const perDiemCustomUnitID = item?.comment?.customUnit?.customUnitID; - - return canSubmitPerDiemExpenseFromWorkspace(policy) && (!perDiemCustomUnitID || perDiemCustomUnitID === workspacePerDiemUnit?.customUnitID); + const getUnreportedTransactions = useCallback( + (transactions: OnyxCollection) => { + if (!transactions) { + return []; } - - return true; - }); - } + return Object.values(transactions || {}).filter((item) => { + const isUnreported = item?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID || item?.reportID === ''; + if (!isUnreported) { + return false; + } + + if (isPerDiemRequest(item)) { + // Only show per diem expenses if the target workspace has per diem enabled and the per diem expense was created in the same workspace + const workspacePerDiemUnit = getPerDiemCustomUnit(policy); + const perDiemCustomUnitID = item?.comment?.customUnit?.customUnitID; + + return canSubmitPerDiemExpenseFromWorkspace(policy) && (!perDiemCustomUnitID || perDiemCustomUnitID === workspacePerDiemUnit?.customUnitID); + } + + return true; + }); + }, + [policy], + ); const [transactions = getEmptyArray()] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { - selector: (_transactions) => getUnreportedTransactions(_transactions), + selector: getUnreportedTransactions, canBeMissing: true, }); diff --git a/src/pages/Search/EmptySearchView.tsx b/src/pages/Search/EmptySearchView.tsx index 67e2b3f0d703..8f9e721358d9 100644 --- a/src/pages/Search/EmptySearchView.tsx +++ b/src/pages/Search/EmptySearchView.tsx @@ -42,7 +42,7 @@ import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {IntroSelected, PersonalDetails, Policy} from '@src/types/onyx'; +import type {IntroSelected, PersonalDetails, Policy, Transaction} from '@src/types/onyx'; import type {SearchDataTypes} from '@src/types/onyx/SearchResults'; type EmptySearchViewProps = { @@ -129,6 +129,9 @@ function EmptySearchView({similarSearchHash, type, groupBy, hasResults}: EmptySe ); } +const hasTransactionsSelector = (transactions: OnyxCollection) => + Object.values(transactions ?? {}).filter((transaction) => transaction?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE).length > 0; + function EmptySearchViewContent({ similarSearchHash, type, @@ -153,7 +156,7 @@ function EmptySearchViewContent({ const [hasTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { canBeMissing: true, - selector: (transactions) => Object.values(transactions ?? {}).filter((transaction) => transaction?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE).length > 0, + selector: hasTransactionsSelector, }); const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRY_NEW_DOT, {selector: tryNewDotOnyxSelector, canBeMissing: true}); diff --git a/src/pages/TransactionDuplicate/Confirmation.tsx b/src/pages/TransactionDuplicate/Confirmation.tsx index 99ff16fc3fe3..000c3930cd49 100644 --- a/src/pages/TransactionDuplicate/Confirmation.tsx +++ b/src/pages/TransactionDuplicate/Confirmation.tsx @@ -1,7 +1,7 @@ import {useRoute} from '@react-navigation/native'; import React, {useCallback, useMemo} from 'react'; import {View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import Button from '@components/Button'; import FixedFooter from '@components/FixedFooter'; @@ -17,6 +17,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation'; import useThemeStyles from '@hooks/useThemeStyles'; +import useTransactionsByID from '@hooks/useTransactionsByID'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -52,14 +53,7 @@ function Confirmation() { () => transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], [transactionViolations], ); - const [allDuplicates] = useOnyx( - ONYXKEYS.COLLECTION.TRANSACTION, - { - selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), - canBeMissing: true, - }, - [allDuplicateIDs], - ); + const [allDuplicates] = useTransactionsByID(allDuplicateIDs); const compareResult = TransactionUtils.compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID); const {goBack} = useReviewDuplicatesNavigation(Object.keys(compareResult.change ?? {}), 'confirmation', route.params.threadReportID, route.params.backTo); @@ -69,10 +63,17 @@ function Confirmation() { const reportAction = Object.values(reportActions ?? {}).find( (action) => ReportActionsUtils.isMoneyRequestAction(action) && ReportActionsUtils.getOriginalMessage(action)?.IOUTransactionID === reviewDuplicates?.transactionID, ); + const duplicatesSelector = useCallback( + (allTransactions: OnyxCollection) => { + return reviewDuplicates?.duplicates.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]); + }, + [reviewDuplicates?.duplicates], + ); + const [duplicates] = useOnyx( ONYXKEYS.COLLECTION.TRANSACTION, { - selector: (allTransactions) => reviewDuplicates?.duplicates.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), + selector: duplicatesSelector, canBeMissing: true, }, [reviewDuplicates?.duplicates], diff --git a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx index 60fe08e7cc74..aac4c910a8bb 100644 --- a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx +++ b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx @@ -19,6 +19,8 @@ type DuplicateTransactionItemProps = { policies: OnyxCollection; }; +const linkedTransactionRouteErrorSelector = (transaction: OnyxEntry) => transaction?.errorFields?.route ?? null; + function DuplicateTransactionItem({transaction, index, allReports, policies}: DuplicateTransactionItemProps) { const styles = useThemeStyles(); const [userWalletTierName] = useOnyx(ONYXKEYS.USER_WALLET, {selector: (wallet) => wallet?.tierName, canBeMissing: false}); @@ -48,7 +50,7 @@ function DuplicateTransactionItem({transaction, index, allReports, policies}: Du const [linkedTransactionRouteError] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${isMoneyRequestAction(action) && getOriginalMessage(action)?.IOUTransactionID}`, { canBeMissing: true, - selector: (transactionItem) => transactionItem?.errorFields?.route ?? null, + selector: linkedTransactionRouteErrorSelector, }); if (!action || !report) { diff --git a/src/pages/TransactionDuplicate/ReviewBillable.tsx b/src/pages/TransactionDuplicate/ReviewBillable.tsx index e4f51273b660..fba682d85a09 100644 --- a/src/pages/TransactionDuplicate/ReviewBillable.tsx +++ b/src/pages/TransactionDuplicate/ReviewBillable.tsx @@ -5,6 +5,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation'; +import useTransactionsByID from '@hooks/useTransactionsByID'; import {setReviewDuplicatesKey} from '@libs/actions/Transaction'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -29,14 +30,8 @@ function ReviewBillable() { () => transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], [transactionViolations], ); - const [allDuplicates] = useOnyx( - ONYXKEYS.COLLECTION.TRANSACTION, - { - selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), - canBeMissing: true, - }, - [allDuplicateIDs], - ); + + const [allDuplicates] = useTransactionsByID(allDuplicateIDs); const compareResult = compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation( diff --git a/src/pages/TransactionDuplicate/ReviewCategory.tsx b/src/pages/TransactionDuplicate/ReviewCategory.tsx index 3d8a0851a6ba..881baef90051 100644 --- a/src/pages/TransactionDuplicate/ReviewCategory.tsx +++ b/src/pages/TransactionDuplicate/ReviewCategory.tsx @@ -5,6 +5,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation'; +import useTransactionsByID from '@hooks/useTransactionsByID'; import {setReviewDuplicatesKey} from '@libs/actions/Transaction'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -29,14 +30,7 @@ function ReviewCategory() { () => transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], [transactionViolations], ); - const [allDuplicates] = useOnyx( - ONYXKEYS.COLLECTION.TRANSACTION, - { - selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), - canBeMissing: true, - }, - [allDuplicateIDs], - ); + const [allDuplicates] = useTransactionsByID(allDuplicateIDs); const compareResult = compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); diff --git a/src/pages/TransactionDuplicate/ReviewDescription.tsx b/src/pages/TransactionDuplicate/ReviewDescription.tsx index 01b3f5b4a332..efba85800ed4 100644 --- a/src/pages/TransactionDuplicate/ReviewDescription.tsx +++ b/src/pages/TransactionDuplicate/ReviewDescription.tsx @@ -5,6 +5,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation'; +import useTransactionsByID from '@hooks/useTransactionsByID'; import {setReviewDuplicatesKey} from '@libs/actions/Transaction'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -31,14 +32,7 @@ function ReviewDescription() { () => transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], [transactionViolations], ); - const [allDuplicates] = useOnyx( - ONYXKEYS.COLLECTION.TRANSACTION, - { - selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), - canBeMissing: true, - }, - [allDuplicateIDs], - ); + const [allDuplicates] = useTransactionsByID(allDuplicateIDs); const compareResult = compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); diff --git a/src/pages/TransactionDuplicate/ReviewMerchant.tsx b/src/pages/TransactionDuplicate/ReviewMerchant.tsx index 36bd7b8e6c8d..e4a657a38ac6 100644 --- a/src/pages/TransactionDuplicate/ReviewMerchant.tsx +++ b/src/pages/TransactionDuplicate/ReviewMerchant.tsx @@ -5,6 +5,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation'; +import useTransactionsByID from '@hooks/useTransactionsByID'; import {setReviewDuplicatesKey} from '@libs/actions/Transaction'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -29,14 +30,7 @@ function ReviewMerchant() { () => transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], [transactionViolations], ); - const [allDuplicates] = useOnyx( - ONYXKEYS.COLLECTION.TRANSACTION, - { - selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), - canBeMissing: true, - }, - [allDuplicateIDs], - ); + const [allDuplicates] = useTransactionsByID(allDuplicateIDs); const compareResult = compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); diff --git a/src/pages/TransactionDuplicate/ReviewReimbursable.tsx b/src/pages/TransactionDuplicate/ReviewReimbursable.tsx index 42fed32465f4..15d8765d3858 100644 --- a/src/pages/TransactionDuplicate/ReviewReimbursable.tsx +++ b/src/pages/TransactionDuplicate/ReviewReimbursable.tsx @@ -5,6 +5,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation'; +import useTransactionsByID from '@hooks/useTransactionsByID'; import {setReviewDuplicatesKey} from '@libs/actions/Transaction'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -29,14 +30,7 @@ function ReviewReimbursable() { () => transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], [transactionViolations], ); - const [allDuplicates] = useOnyx( - ONYXKEYS.COLLECTION.TRANSACTION, - { - selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), - canBeMissing: true, - }, - [allDuplicateIDs], - ); + const [allDuplicates] = useTransactionsByID(allDuplicateIDs); const compareResult = compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); diff --git a/src/pages/TransactionDuplicate/ReviewTag.tsx b/src/pages/TransactionDuplicate/ReviewTag.tsx index 6204d56e43fc..c316dcd506f8 100644 --- a/src/pages/TransactionDuplicate/ReviewTag.tsx +++ b/src/pages/TransactionDuplicate/ReviewTag.tsx @@ -5,6 +5,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation'; +import useTransactionsByID from '@hooks/useTransactionsByID'; import {setReviewDuplicatesKey} from '@libs/actions/Transaction'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -31,15 +32,7 @@ function ReviewTag() { () => transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], [transactionViolations], ); - const [allDuplicates] = useOnyx( - ONYXKEYS.COLLECTION.TRANSACTION, - { - selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), - canBeMissing: true, - }, - [allDuplicateIDs], - ); - + const [allDuplicates] = useTransactionsByID(allDuplicateIDs); const compareResult = compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation( diff --git a/src/pages/TransactionDuplicate/ReviewTaxCode.tsx b/src/pages/TransactionDuplicate/ReviewTaxCode.tsx index 319e90f3f423..be38ccefa070 100644 --- a/src/pages/TransactionDuplicate/ReviewTaxCode.tsx +++ b/src/pages/TransactionDuplicate/ReviewTaxCode.tsx @@ -5,6 +5,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation'; +import useTransactionsByID from '@hooks/useTransactionsByID'; import {setReviewDuplicatesKey} from '@libs/actions/Transaction'; import {convertToBackendAmount} from '@libs/CurrencyUtils'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; @@ -36,14 +37,7 @@ function ReviewTaxRate() { () => transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], [transactionViolations], ); - const [allDuplicates] = useOnyx( - ONYXKEYS.COLLECTION.TRANSACTION, - { - selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), - canBeMissing: true, - }, - [allDuplicateIDs], - ); + const [allDuplicates] = useTransactionsByID(allDuplicateIDs); const compareResult = compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); From fd1d30f19d926a80ddeb703781c0b4252b12e110 Mon Sep 17 00:00:00 2001 From: mkzie2 Date: Thu, 18 Sep 2025 22:01:39 +0700 Subject: [PATCH 2/3] add canBeMissing --- src/hooks/useTransactionsByID.ts | 1 + src/hooks/useTripTransactions.ts | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/hooks/useTransactionsByID.ts b/src/hooks/useTransactionsByID.ts index 629bba47ccdf..cbb41dd4ed8f 100644 --- a/src/hooks/useTransactionsByID.ts +++ b/src/hooks/useTransactionsByID.ts @@ -12,6 +12,7 @@ function useTransactionsByID(transactionIDs: string[]) { const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { selector: transactionsSelector, + canBeMissing: true, }); return [transactions]; diff --git a/src/hooks/useTripTransactions.ts b/src/hooks/useTripTransactions.ts index 23794f04c32c..11fe871c9a99 100644 --- a/src/hooks/useTripTransactions.ts +++ b/src/hooks/useTripTransactions.ts @@ -21,6 +21,7 @@ function useTripTransactions(reportID: string | undefined): Transaction[] { Object.values(reports ?? {}) .filter((report) => report && report.chatReportID === reportID) .map((report) => report?.reportID), + canBeMissing: true, }); const tripTransactionsSelector = useCallback( @@ -38,6 +39,7 @@ function useTripTransactions(reportID: string | undefined): Transaction[] { ONYXKEYS.COLLECTION.TRANSACTION, { selector: tripTransactionsSelector, + canBeMissing: true, }, [tripTransactionReportIDs], ); From 546a0a32c882fb795b51bbf68e8216464fb4941f Mon Sep 17 00:00:00 2001 From: mkzie2 Date: Mon, 22 Sep 2025 11:48:17 +0700 Subject: [PATCH 3/3] add selector as dependency of useOnyx --- src/hooks/useReportTransactions.ts | 12 ++++++++---- src/hooks/useTransactionsByID.ts | 12 ++++++++---- src/hooks/useTripTransactions.ts | 2 +- src/pages/AddUnreportedExpense.tsx | 12 ++++++++---- src/pages/TransactionDuplicate/Confirmation.tsx | 17 ++--------------- 5 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/hooks/useReportTransactions.ts b/src/hooks/useReportTransactions.ts index f7a951d5d4a1..800ce0a99f7d 100644 --- a/src/hooks/useReportTransactions.ts +++ b/src/hooks/useReportTransactions.ts @@ -20,10 +20,14 @@ function useReportTransactions(reportID: string | undefined): Transaction[] { [reportID], ); - const [reportTransactions = getEmptyArray()] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { - selector: reportTransactionsSelector, - canBeMissing: true, - }); + const [reportTransactions = getEmptyArray()] = useOnyx( + ONYXKEYS.COLLECTION.TRANSACTION, + { + selector: reportTransactionsSelector, + canBeMissing: true, + }, + [reportTransactionsSelector], + ); return reportTransactions; } diff --git a/src/hooks/useTransactionsByID.ts b/src/hooks/useTransactionsByID.ts index cbb41dd4ed8f..049eb4d5fdf4 100644 --- a/src/hooks/useTransactionsByID.ts +++ b/src/hooks/useTransactionsByID.ts @@ -10,10 +10,14 @@ function useTransactionsByID(transactionIDs: string[]) { [transactionIDs], ); - const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { - selector: transactionsSelector, - canBeMissing: true, - }); + const [transactions] = useOnyx( + ONYXKEYS.COLLECTION.TRANSACTION, + { + selector: transactionsSelector, + canBeMissing: true, + }, + [transactionsSelector], + ); return [transactions]; } diff --git a/src/hooks/useTripTransactions.ts b/src/hooks/useTripTransactions.ts index 11fe871c9a99..5df3587d2317 100644 --- a/src/hooks/useTripTransactions.ts +++ b/src/hooks/useTripTransactions.ts @@ -41,7 +41,7 @@ function useTripTransactions(reportID: string | undefined): Transaction[] { selector: tripTransactionsSelector, canBeMissing: true, }, - [tripTransactionReportIDs], + [tripTransactionsSelector], ); return tripTransactions; } diff --git a/src/pages/AddUnreportedExpense.tsx b/src/pages/AddUnreportedExpense.tsx index f70f5f379a38..a8601ed67653 100644 --- a/src/pages/AddUnreportedExpense.tsx +++ b/src/pages/AddUnreportedExpense.tsx @@ -81,10 +81,14 @@ function AddUnreportedExpense({route}: AddUnreportedExpensePageType) { [policy], ); - const [transactions = getEmptyArray()] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { - selector: getUnreportedTransactions, - canBeMissing: true, - }); + const [transactions = getEmptyArray()] = useOnyx( + ONYXKEYS.COLLECTION.TRANSACTION, + { + selector: getUnreportedTransactions, + canBeMissing: true, + }, + [getUnreportedTransactions], + ); const fetchMoreUnreportedTransactions = () => { if (!hasMoreUnreportedTransactionsResults || isLoadingUnreportedTransactions) { diff --git a/src/pages/TransactionDuplicate/Confirmation.tsx b/src/pages/TransactionDuplicate/Confirmation.tsx index 000c3930cd49..3063feb31674 100644 --- a/src/pages/TransactionDuplicate/Confirmation.tsx +++ b/src/pages/TransactionDuplicate/Confirmation.tsx @@ -1,7 +1,7 @@ import {useRoute} from '@react-navigation/native'; import React, {useCallback, useMemo} from 'react'; import {View} from 'react-native'; -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import Button from '@components/Button'; import FixedFooter from '@components/FixedFooter'; @@ -63,21 +63,8 @@ function Confirmation() { const reportAction = Object.values(reportActions ?? {}).find( (action) => ReportActionsUtils.isMoneyRequestAction(action) && ReportActionsUtils.getOriginalMessage(action)?.IOUTransactionID === reviewDuplicates?.transactionID, ); - const duplicatesSelector = useCallback( - (allTransactions: OnyxCollection) => { - return reviewDuplicates?.duplicates.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]); - }, - [reviewDuplicates?.duplicates], - ); - const [duplicates] = useOnyx( - ONYXKEYS.COLLECTION.TRANSACTION, - { - selector: duplicatesSelector, - canBeMissing: true, - }, - [reviewDuplicates?.duplicates], - ); + const [duplicates] = useTransactionsByID(reviewDuplicates?.duplicates ?? []); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, {canBeMissing: true}); const transactionsMergeParams = useMemo( () => TransactionUtils.buildMergeDuplicatesParams(reviewDuplicates, duplicates ?? [], newTransaction),