Skip to content

Commit c1834e8

Browse files
fix: deep-scan dirtyFields to avoid false Save Changes prompt after undo
1 parent 6e8252e commit c1834e8

1 file changed

Lines changed: 13 additions & 3 deletions

File tree

src/modules/Builder/features/SaveAndPublish/SaveAndPublish.hooks.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,9 +319,19 @@ export const useSaveAndPublishSetup = (): SaveAndPublishSetup => {
319319
// `isDirty` is a structural deep-compare of the form values against the form defaults,
320320
// so empty arrays that field arrays (e.g. `responseValues.rows`) inject into the form
321321
// values on mount — but never into the defaults — make `isDirty` permanently `true`
322-
// even with no real changes. `dirtyFields` stays empty in that case, so it accurately
323-
// reflects whether there are unsaved user changes worth prompting about.
324-
const hasFormChanges = !!Object.keys(dirtyFields ?? {}).length;
322+
// even with no real changes.
323+
//
324+
// A shallow `Object.keys(dirtyFields).length` check is also insufficient: after a
325+
// useFieldArray append+remove round-trip, RHF leaves stale array-shaped entries in
326+
// `dirtyFields` (e.g. `{ activities: [{ name: false, … }] }`) even though every leaf
327+
// is `false`. Recursively scanning for any `true` leaf is the correct signal.
328+
const hasTrueDirtyField = (val: unknown): boolean => {
329+
if (val === true) return true;
330+
if (!val || typeof val !== 'object') return false;
331+
332+
return Object.values(val).some(hasTrueDirtyField);
333+
};
334+
const hasFormChanges = hasTrueDirtyField(dirtyFields);
325335
const {
326336
cancelNavigation: onCancelNavigation,
327337
confirmNavigation,

0 commit comments

Comments
 (0)