From df004c7277b87e5313bc4a6941823a8e4f4c5d45 Mon Sep 17 00:00:00 2001 From: "Abdelrahman Khattab (via MelvinBot)" Date: Wed, 4 Mar 2026 23:08:17 +0000 Subject: [PATCH 1/3] Add missing navigateBack() calls in merchant step submit handler The updateMerchant function saves data but never navigates back to the confirmation page, leaving users stuck on the merchant page. This adds navigateBack() to all three code paths, matching the pattern used by other working step pages (Date, Tag, Category). Co-authored-by: Abdelrahman Khattab --- src/pages/iou/request/step/IOURequestStepMerchant.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepMerchant.tsx b/src/pages/iou/request/step/IOURequestStepMerchant.tsx index 838e71c5ede7..57a6fccc9831 100644 --- a/src/pages/iou/request/step/IOURequestStepMerchant.tsx +++ b/src/pages/iou/request/step/IOURequestStepMerchant.tsx @@ -109,10 +109,12 @@ function IOURequestStepMerchant({ if (isEditingSplitBill) { setDraftSplitTransaction(transactionID, splitDraftTransaction, {merchant: newMerchant}); + navigateBack(); return; } if (newMerchant === merchant || (newMerchant === '' && merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT)) { + navigateBack(); return; } setMoneyRequestMerchant(transactionID, newMerchant || CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, !isEditing); @@ -131,6 +133,8 @@ function IOURequestStepMerchant({ parentReportNextStep, }); } + + navigateBack(); }; return ( From 7f6d05c7f8eba8a62817a8788da0d4f73083f62c Mon Sep 17 00:00:00 2001 From: "Abdelrahman Khattab (via MelvinBot)" Date: Wed, 4 Mar 2026 23:18:49 +0000 Subject: [PATCH 2/3] Use useEffect to defer navigation until after hasUnsavedChanges renders The synchronous navigateBack() calls were blocked by DiscardChangesConfirmation's usePreventRemove because setHasUnsavedChanges(false) hadn't rendered yet. Use a useEffect that fires after the state update renders, matching the pattern that PR #78291 inadvertently removed. Co-authored-by: Abdelrahman Khattab --- .../iou/request/step/IOURequestStepMerchant.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepMerchant.tsx b/src/pages/iou/request/step/IOURequestStepMerchant.tsx index 57a6fccc9831..299f4b1244d1 100644 --- a/src/pages/iou/request/step/IOURequestStepMerchant.tsx +++ b/src/pages/iou/request/step/IOURequestStepMerchant.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useState} from 'react'; +import React, {useCallback, useEffect, useState} from 'react'; import {InteractionManager, View} from 'react-native'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; @@ -66,6 +66,7 @@ function IOURequestStepMerchant({ const {isBetaEnabled} = usePermissions(); const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT); const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); + const [isSaved, setIsSaved] = useState(false); const updateHasUnsavedChanges = useCallback( (value: string) => { @@ -84,6 +85,13 @@ function IOURequestStepMerchant({ Navigation.goBack(backTo); }, [backTo]); + useEffect(() => { + if (!isSaved) { + return; + } + navigateBack(); + }, [isSaved, navigateBack]); + const validate = useCallback( (value: FormOnyxValues) => { const errors: FormInputErrors = {}; @@ -109,12 +117,12 @@ function IOURequestStepMerchant({ if (isEditingSplitBill) { setDraftSplitTransaction(transactionID, splitDraftTransaction, {merchant: newMerchant}); - navigateBack(); + setIsSaved(true); return; } if (newMerchant === merchant || (newMerchant === '' && merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT)) { - navigateBack(); + setIsSaved(true); return; } setMoneyRequestMerchant(transactionID, newMerchant || CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, !isEditing); @@ -133,8 +141,7 @@ function IOURequestStepMerchant({ parentReportNextStep, }); } - - navigateBack(); + setIsSaved(true); }; return ( From 40da976a40891da5f1ae417eee649f4495c688cb Mon Sep 17 00:00:00 2001 From: "{\"message\":\"Not Found\",\"documentation_url\":\"https://docs.github.com/rest/issues/comments#get-an-issue-comment\",\"status\":\"404\"} (via MelvinBot)" Date: Wed, 4 Mar 2026 23:49:47 +0000 Subject: [PATCH 3/3] Fix: Add missing navigateBack in description step after save Apply the same deferred navigateBack pattern used in IOURequestStepMerchant to IOURequestStepDescription. The description step had the same bug where the user was stranded on the page after saving because navigateBack() was never called. Uses isSaved state + useEffect to defer navigation until after the render where hasUnsavedChanges becomes false, preventing DiscardChangesConfirmation from blocking navigation. Co-authored-by: {"message":"Not Found","documentation_url":"https://docs.github.com/rest/issues/comments#get-an-issue-comment","status":"404"} <{"message":"Not Found","documentation_url":"https://docs.github.com/rest/issues/comments#get-an-issue-comment","status":"404"}@users.noreply.github.com> --- .../iou/request/step/IOURequestStepDescription.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepDescription.tsx b/src/pages/iou/request/step/IOURequestStepDescription.tsx index 203e6fea9a5d..1d0987a382f5 100644 --- a/src/pages/iou/request/step/IOURequestStepDescription.tsx +++ b/src/pages/iou/request/step/IOURequestStepDescription.tsx @@ -1,5 +1,5 @@ import lodashIsEmpty from 'lodash/isEmpty'; -import React, {useCallback, useMemo, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {InteractionManager, View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; @@ -74,6 +74,7 @@ function IOURequestStepDescription({ }, [isTransactionDraft, iouType, isEditingSplit, splitDraftTransaction, transaction?.comment?.comment]); const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); + const [isSaved, setIsSaved] = useState(false); useRestartOnReceiptFailure(transaction, reportID, iouType, action); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const currentUserAccountIDParam = currentUserPersonalDetails.accountID; @@ -119,6 +120,13 @@ function IOURequestStepDescription({ Navigation.goBack(backTo); }, [backTo]); + useEffect(() => { + if (!isSaved) { + return; + } + navigateBack(); + }, [isSaved, navigateBack]); + const updateHasUnsavedChanges = useCallback( (value: string) => { if (value === currentDescriptionInMarkdown) { @@ -139,11 +147,13 @@ function IOURequestStepDescription({ const newComment = value.moneyRequestComment.trim(); if (newComment === currentDescriptionInMarkdown) { + setIsSaved(true); return; } if (isEditingSplit) { setDraftSplitTransaction(transaction?.transactionID, splitDraftTransaction, {comment: newComment}); + setIsSaved(true); return; } @@ -164,6 +174,7 @@ function IOURequestStepDescription({ parentReportNextStep, }); } + setIsSaved(true); }; // eslint-disable-next-line rulesdir/no-negated-variables