Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/hooks/useSearchAutoRefetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ function useSearchAutoRefetch({

// Trigger search when a new report action is added while on chat or when a new transaction is added for the other search types.
useEffect(() => {
// Snapshot and focus updates re-run this effect with unchanged collections, which would diff a collection against itself.
if (transactions === previousTransactions && reportActions === previousReportActions && !hasPendingSearchRef.current) {
return;
}

const previousTransactionIDsLocal = Object.keys(previousTransactions ?? {});
const transactionsIDs = Object.keys(transactions ?? {});

Expand All @@ -106,7 +111,7 @@ function useSearchAutoRefetch({
// The rows and the footer total are served from the Search snapshot, and the footer total is computed by the
// server, so those edits only become visible after a refetch.
const hasChangedResultTransaction =
!isChat && !hasTransactionsIDsChange && hasChangedTransactionInSearchResults(transactions, previousTransactions, previousTransactionsIDsSet, searchResultsData);
!isChat && !hasTransactionsIDsChange && hasChangedTransactionInSearchResults(transactions, transactionsIDs, previousTransactions, previousTransactionsIDsSet, searchResultsData);

// Check if there is a change in the transactions or report actions list
if ((isChat ? hasReportActionsIDsChange : hasTransactionsIDsChange || hasChangedResultTransaction) || hasPendingSearchRef.current) {
Expand Down Expand Up @@ -258,6 +263,7 @@ function extractReportActionIDsFromSearchResults(searchResultsData: Partial<Sear
*/
function hasChangedTransactionInSearchResults(
transactions: OnyxCollection<Transaction>,
transactionKeys: string[],
previousTransactions: OnyxCollection<Transaction>,
previousTransactionKeys: Set<string>,
searchResultsData: Partial<SearchResults['data']> | undefined,
Expand All @@ -269,7 +275,8 @@ function hasChangedTransactionInSearchResults(
const isReportInSearchResults = (reportID: string | undefined) => !!reportID && !!searchResultsData[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`];

const changedTransactionIDs: string[] = [];
for (const [key, transaction] of Object.entries(transactions ?? {})) {
for (const key of transactionKeys) {
const transaction = transactions?.[key];
const previousTransaction = previousTransactions?.[key];
if (!transaction?.transactionID || !previousTransactionKeys.has(key) || previousTransaction === transaction) {
continue;
Expand Down
82 changes: 82 additions & 0 deletions tests/unit/useSearchAutoRefetchTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,88 @@ describe('useSearchAutoRefetch', () => {
expect(search).toHaveBeenCalledTimes(1);
});

it('should not trigger search when only the snapshot changes and the collections are unchanged', () => {
// Given Search rendered with a transaction the results show, where `usePrevious` has caught up so both collections are the same object
const reportActions = {};
const transactions = {transactions_1: createMock<Transaction>({transactionID: '1', amount: 100})};
const initialProps = createMock<UseSearchAutoRefetch>({
...baseProps,
searchResults: {
...baseProps.searchResults,
data: {
transactions_1: {transactionID: '1'},
},
},
transactions,
previousTransactions: transactions,
reportActions,
previousReportActions: reportActions,
});

const {rerender} = renderHook((props: UseSearchAutoRefetch) => useSearchAutoRefetch(props), {
initialProps,
});

// When a new snapshot arrives without any transaction changing, which re-runs the effect through its `searchResultsData` dependency
rerender(
createMock<UseSearchAutoRefetch>({
...initialProps,
searchResults: {
...baseProps.searchResults,
data: {
transactions_1: {transactionID: '1'},
transactions_2: {transactionID: '2'},
},
},
}),
);

// Then no search is triggered, because nothing in the collections changed since the last run
expect(search).not.toHaveBeenCalled();
});

it('should trigger the deferred search when the collections are the same object once Search is active again', () => {
// Given Search rendered offline with a transaction the results show and no report action changes
const reportActions = {};
const transaction = createMock<Transaction>({transactionID: '1', amount: 100});
const editedTransaction = createMock<Transaction>({transactionID: '1', amount: 250});
const initialProps = createMock<UseSearchAutoRefetch>({
...baseProps,
searchResults: {
...baseProps.searchResults,
data: {
transactions_1: {transactionID: '1'},
},
},
transactions: {transactions_1: transaction},
previousTransactions: {transactions_1: transaction},
reportActions,
previousReportActions: reportActions,
});

mockIsOffline = true;
const {rerender} = renderHook((props: UseSearchAutoRefetch) => useSearchAutoRefetch(props), {
initialProps,
});

// When the transaction is edited while offline, which defers the search
const editedTransactions = {transactions_1: editedTransaction};
rerender(createMock<UseSearchAutoRefetch>({...initialProps, transactions: editedTransactions}));
expect(search).not.toHaveBeenCalled();

// And `usePrevious` catches up, so both collections are the same object like they are in Search
const settledProps = createMock<UseSearchAutoRefetch>({...initialProps, transactions: editedTransactions, previousTransactions: editedTransactions});
rerender(settledProps);
expect(search).not.toHaveBeenCalled();

// And the network comes back
mockIsOffline = false;
rerender({...settledProps});

// Then the deferred search still runs, because unchanged collections must not swallow a pending refetch
expect(search).toHaveBeenCalledTimes(1);
});

it('should not trigger search on a non-chat search when a report action was added and Onyx holds a transaction the query filters out', () => {
const transaction1 = createMock<Transaction>({transactionID: '1'});
const transaction99 = createMock<Transaction>({transactionID: '99'});
Expand Down
Loading