From c409b74c377cdfb50d6919ecf4f6c1dfdeb2cd47 Mon Sep 17 00:00:00 2001 From: Povilas Zirgulis Date: Wed, 25 Jun 2025 18:22:14 +0300 Subject: [PATCH 1/7] reduce state updates in onLayout --- .../MoneyRequestReportPreview/index.tsx | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx index 033d6a0babe0..86a7b6fcd1ae 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useMemo, useState} from 'react'; +import React, {useCallback, useMemo, useRef, useState} from 'react'; import type {LayoutChangeEvent, ListRenderItem} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import TransactionPreview from '@components/ReportActionItem/TransactionPreview'; @@ -55,11 +55,23 @@ function MoneyRequestReportPreview({ const lastTransactionViolations = useTransactionViolations(lastTransaction?.transactionID); const isTrackExpenseAction = isTrackExpenseActionReportActionsUtils(action); const isSplitBillAction = isSplitBillActionReportActionsUtils(action); - const [currentWidth, setCurrentWidth] = useState(0); - const [currentWrapperWidth, setCurrentWrapperWidth] = useState(0); + + const currentWidth = useRef(0); + const currentWrapperWidth = useRef(0); + const [isMoneyPreviewContentReady, setIsMoneyPreviewContentReady] = useState(false); + + const onLayout = () => { + if (!currentWidth.current || !currentWrapperWidth.current || isMoneyPreviewContentReady) { + return; + } + setIsMoneyPreviewContentReady(true); + }; + const reportPreviewStyles = useMemo( - () => StyleUtils.getMoneyRequestReportPreviewStyle(shouldUseNarrowLayout, transactions.length, currentWidth, currentWrapperWidth), - [StyleUtils, currentWidth, currentWrapperWidth, shouldUseNarrowLayout, transactions.length], + () => StyleUtils.getMoneyRequestReportPreviewStyle(shouldUseNarrowLayout, transactions.length, currentWidth?.current, currentWrapperWidth?.current), + // eslint-disable-next-line react-compiler/react-compiler + // eslint-disable-next-line react-hooks/exhaustive-deps + [StyleUtils, shouldUseNarrowLayout, transactions.length, isMoneyPreviewContentReady], ); const shouldShowIOUData = useMemo(() => { @@ -125,12 +137,14 @@ function MoneyRequestReportPreview({ lastTransactionViolations={lastTransactionViolations} renderTransactionItem={renderItem} onCarouselLayout={(e: LayoutChangeEvent) => { - setCurrentWidth(e.nativeEvent.layout.width); + currentWidth.current = e.nativeEvent.layout.width; + onLayout(); }} onWrapperLayout={(e: LayoutChangeEvent) => { - setCurrentWrapperWidth(e.nativeEvent.layout.width); + currentWrapperWidth.current = e.nativeEvent.layout.width; + onLayout(); }} - currentWidth={currentWidth} + currentWidth={currentWidth?.current} reportPreviewStyles={reportPreviewStyles} shouldDisplayContextMenu={shouldDisplayContextMenu} isInvoice={isInvoice} From 95b17c5f01315fd627d5a9e129e9d5415275a557 Mon Sep 17 00:00:00 2001 From: Povilas Zirgulis Date: Wed, 25 Jun 2025 19:00:59 +0300 Subject: [PATCH 2/7] memoize onLayout functions --- .../MoneyRequestReportPreview/index.tsx | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx index 86a7b6fcd1ae..b4b053477b2e 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx @@ -60,12 +60,12 @@ function MoneyRequestReportPreview({ const currentWrapperWidth = useRef(0); const [isMoneyPreviewContentReady, setIsMoneyPreviewContentReady] = useState(false); - const onLayout = () => { + const onLayout = useCallback(() => { if (!currentWidth.current || !currentWrapperWidth.current || isMoneyPreviewContentReady) { return; } setIsMoneyPreviewContentReady(true); - }; + }, [isMoneyPreviewContentReady]); const reportPreviewStyles = useMemo( () => StyleUtils.getMoneyRequestReportPreviewStyle(shouldUseNarrowLayout, transactions.length, currentWidth?.current, currentWrapperWidth?.current), @@ -93,6 +93,20 @@ function MoneyRequestReportPreview({ Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(iouReportID, undefined, undefined, undefined, undefined, Navigation.getActiveRoute())); }, [iouReportID]); + const onCarouselLayout = useCallback(() => { + return (e: LayoutChangeEvent) => { + currentWidth.current = e.nativeEvent.layout.width; + onLayout(); + }; + }, [onLayout]); + + const onWrapperLayout = useCallback(() => { + return (e: LayoutChangeEvent) => { + currentWrapperWidth.current = e.nativeEvent.layout.width; + onLayout(); + }; + }, [onLayout]); + const renderItem: ListRenderItem = ({item}) => ( { - currentWidth.current = e.nativeEvent.layout.width; - onLayout(); - }} - onWrapperLayout={(e: LayoutChangeEvent) => { - currentWrapperWidth.current = e.nativeEvent.layout.width; - onLayout(); - }} + onCarouselLayout={onCarouselLayout} + onWrapperLayout={onWrapperLayout} currentWidth={currentWidth?.current} reportPreviewStyles={reportPreviewStyles} shouldDisplayContextMenu={shouldDisplayContextMenu} From c2274ca34aed1dc33a9e3ecb17610a4adedd87c9 Mon Sep 17 00:00:00 2001 From: Povilas Zirgulis Date: Tue, 8 Jul 2025 17:18:51 +0300 Subject: [PATCH 3/7] fix tests --- .../MoneyRequestReportPreview/index.tsx | 18 ++++++++++-------- tests/ui/MoneyRequestReportPreview.test.tsx | 7 ++++++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx index aa9238784ae9..304662de4f0a 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx @@ -90,19 +90,21 @@ function MoneyRequestReportPreview({ Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(iouReportID, undefined, undefined, undefined, undefined, Navigation.getActiveRoute())); }, [iouReportID]); - const onCarouselLayout = useCallback(() => { - return (e: LayoutChangeEvent) => { + const onCarouselLayout = useCallback( + (e: LayoutChangeEvent) => { currentWidth.current = e.nativeEvent.layout.width; onLayout(); - }; - }, [onLayout]); + }, + [onLayout], + ); - const onWrapperLayout = useCallback(() => { - return (e: LayoutChangeEvent) => { + const onWrapperLayout = useCallback( + (e: LayoutChangeEvent) => { currentWrapperWidth.current = e.nativeEvent.layout.width; onLayout(); - }; - }, [onLayout]); + }, + [onLayout], + ); const renderItem: ListRenderItem = ({item}) => ( { renderPage({}); await waitForBatchedUpdatesWithAct(); setCurrentWidth(); + + await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${mockTransaction.transactionID}`, {} as OnyxMergeInput<`transactions_${string}`>); + await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${mockSecondTransactionID}`, {} as OnyxMergeInput<`transactions_${string}`>); + await waitForBatchedUpdatesWithAct(); + expect(screen.getAllByTestId(TransactionPreviewSkeletonView.displayName)).toHaveLength(2); }); }); From 97138fbcbc1a675c60692855369121dc1938b372 Mon Sep 17 00:00:00 2001 From: Povilas Zirgulis Date: Thu, 10 Jul 2025 18:25:27 +0300 Subject: [PATCH 4/7] fix styles recalculation when shouldUseNarrowLayout --- .../MoneyRequestReportPreview/index.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx index 304662de4f0a..01328622ce5f 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx @@ -58,11 +58,21 @@ function MoneyRequestReportPreview({ const [isMoneyPreviewContentReady, setIsMoneyPreviewContentReady] = useState(false); const onLayout = useCallback(() => { - if (!currentWidth.current || !currentWrapperWidth.current || isMoneyPreviewContentReady) { + if (!currentWidth.current || !currentWrapperWidth.current) { return; } + + if (shouldUseNarrowLayout) { + setIsMoneyPreviewContentReady(!isMoneyPreviewContentReady); + return; + } + + if (isMoneyPreviewContentReady) { + return; + } + setIsMoneyPreviewContentReady(true); - }, [isMoneyPreviewContentReady]); + }, [isMoneyPreviewContentReady, shouldUseNarrowLayout]); const reportPreviewStyles = useMemo( () => StyleUtils.getMoneyRequestReportPreviewStyle(shouldUseNarrowLayout, transactions.length, currentWidth?.current, currentWrapperWidth?.current), From 0b495ad927f2827ca7ba41bf3767ecec868e934d Mon Sep 17 00:00:00 2001 From: Povilas Zirgulis Date: Fri, 11 Jul 2025 11:20:39 +0300 Subject: [PATCH 5/7] compare widths before updating state --- .../MoneyRequestReportPreview/index.tsx | 67 +++++++++---------- 1 file changed, 30 insertions(+), 37 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx index 01328622ce5f..c8e5dfb18509 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx @@ -53,32 +53,41 @@ function MoneyRequestReportPreview({ const isTrackExpenseAction = isTrackExpenseActionReportActionsUtils(action); const isSplitBillAction = isSplitBillActionReportActionsUtils(action); - const currentWidth = useRef(0); - const currentWrapperWidth = useRef(0); - const [isMoneyPreviewContentReady, setIsMoneyPreviewContentReady] = useState(false); + const widthsRef = useRef<{currentWidth: number | null; currentWrapperWidth: number | null}>({currentWidth: null, currentWrapperWidth: null}); + const [widths, setWidths] = useState({currentWidth: 0, currentWrapperWidth: 0}); - const onLayout = useCallback(() => { - if (!currentWidth.current || !currentWrapperWidth.current) { - return; - } - - if (shouldUseNarrowLayout) { - setIsMoneyPreviewContentReady(!isMoneyPreviewContentReady); - return; - } + const updateWidths = useCallback(() => { + const {currentWidth, currentWrapperWidth} = widthsRef.current; - if (isMoneyPreviewContentReady) { - return; + if (currentWidth && currentWrapperWidth) { + setWidths({currentWidth, currentWrapperWidth}); } + }, []); - setIsMoneyPreviewContentReady(true); - }, [isMoneyPreviewContentReady, shouldUseNarrowLayout]); + const onCarouselLayout = useCallback( + (e: LayoutChangeEvent) => { + const newWidth = e.nativeEvent.layout.width; + if (widthsRef.current.currentWidth !== newWidth) { + widthsRef.current.currentWidth = newWidth; + updateWidths(); + } + }, + [updateWidths], + ); + const onWrapperLayout = useCallback( + (e: LayoutChangeEvent) => { + const newWrapperWidth = e.nativeEvent.layout.width; + if (widthsRef.current.currentWrapperWidth !== newWrapperWidth) { + widthsRef.current.currentWrapperWidth = newWrapperWidth; + updateWidths(); + } + }, + [updateWidths], + ); const reportPreviewStyles = useMemo( - () => StyleUtils.getMoneyRequestReportPreviewStyle(shouldUseNarrowLayout, transactions.length, currentWidth?.current, currentWrapperWidth?.current), - // eslint-disable-next-line react-compiler/react-compiler - // eslint-disable-next-line react-hooks/exhaustive-deps - [StyleUtils, shouldUseNarrowLayout, transactions.length, isMoneyPreviewContentReady], + () => StyleUtils.getMoneyRequestReportPreviewStyle(shouldUseNarrowLayout, transactions.length, widths.currentWidth, widths.currentWrapperWidth), + [StyleUtils, widths, shouldUseNarrowLayout, transactions.length], ); const shouldShowPayerAndReceiver = useMemo(() => { @@ -100,22 +109,6 @@ function MoneyRequestReportPreview({ Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(iouReportID, undefined, undefined, undefined, undefined, Navigation.getActiveRoute())); }, [iouReportID]); - const onCarouselLayout = useCallback( - (e: LayoutChangeEvent) => { - currentWidth.current = e.nativeEvent.layout.width; - onLayout(); - }, - [onLayout], - ); - - const onWrapperLayout = useCallback( - (e: LayoutChangeEvent) => { - currentWrapperWidth.current = e.nativeEvent.layout.width; - onLayout(); - }, - [onLayout], - ); - const renderItem: ListRenderItem = ({item}) => ( Date: Fri, 11 Jul 2025 12:05:24 +0300 Subject: [PATCH 6/7] fix tests --- .../MoneyRequestReportPreviewContent.tsx | 2 +- tests/ui/MoneyRequestReportPreview.test.tsx | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index 1313ff1a2b62..d550bdb20a9f 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -595,7 +595,7 @@ function MoneyRequestReportPreviewContent({ }, [carouselTransactions.length]); return ( - + { }; const setCurrentWidth = () => { + fireEvent(screen.getByTestId('MoneyRequestReportPreviewContent-wrapper'), 'layout', { + nativeEvent: {layout: {width: 600}}, + }); fireEvent(screen.getByTestId('carouselWidthSetter'), 'layout', { nativeEvent: {layout: {width: 500}}, }); @@ -147,6 +150,7 @@ describe('MoneyRequestReportPreview', () => { await waitForBatchedUpdatesWithAct(); setCurrentWidth(); await Onyx.mergeCollection(ONYXKEYS.COLLECTION.TRANSACTION, mockOnyxTransactions).then(waitForBatchedUpdates); + await waitForBatchedUpdatesWithAct(); const {reportName: moneyRequestReportPreviewName = ''} = mockChatReport; for (const transaction of arrayOfTransactions) { const {transactionDisplayAmount, transactionHeaderText} = getTransactionDisplayAmountAndHeaderText(transaction); @@ -163,6 +167,7 @@ describe('MoneyRequestReportPreview', () => { await waitForBatchedUpdatesWithAct(); setCurrentWidth(); await Onyx.multiSet({...mockOnyxTransactions, ...mockOnyxViolations}); + await waitForBatchedUpdatesWithAct(); expect(screen.getAllByText(translateLocal('violations.reviewRequired'))).toHaveLength(2); }); @@ -175,6 +180,6 @@ describe('MoneyRequestReportPreview', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${mockSecondTransactionID}`, {} as OnyxMergeInput<`transactions_${string}`>); await waitForBatchedUpdatesWithAct(); - expect(screen.getAllByTestId(TransactionPreviewSkeletonView.displayName)).toHaveLength(2); + expect(screen.getAllByTestId('TransactionPreviewSkeletonView')).toHaveLength(2); }); }); From 1f7d758011feb54301cbb942d346f478807ffc96 Mon Sep 17 00:00:00 2001 From: Povilas Zirgulis Date: Fri, 11 Jul 2025 12:55:31 +0300 Subject: [PATCH 7/7] fix lint --- .../MoneyRequestReportPreviewContent.tsx | 5 ++++- tests/ui/MoneyRequestReportPreview.test.tsx | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index d550bdb20a9f..5eea5c40e0cc 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -595,7 +595,10 @@ function MoneyRequestReportPreviewContent({ }, [carouselTransactions.length]); return ( - +