From e8806db916b0ae030e3ee3ae385dd3286129957a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 5 Aug 2026 14:45:00 +0000 Subject: [PATCH 1/3] fix(media): keep react-hook-form fields registered across reset with React Compiler A plain reset() empties react-hook-form's internal field registry and relies on register() re-running on the next render to re-register every field. React Compiler memoizes the registered-field JSX (keyed on the stable register reference), so after the reset that runs when the asset edit dialog opens, string fields stayed unregistered: typing updated the DOM but never marked the form dirty and Save stayed disabled. Passing keepFieldsRef keeps fields registered while still applying the new values. Also convert inline Footer/Header dialog components to plain JSX so the footer subtree (including the Save button) is not remounted whenever captured form state changes, and drop the react/react-compiler lint suppressions that pattern required. --- .changeset/media-inline-dialog-footers.md | 5 +++++ .../src/components/DialogAssetEdit/index.tsx | 9 +++++++-- .../src/components/DialogConfirm/index.tsx | 17 +++++------------ .../components/DialogFolderCreate/index.tsx | 14 ++++---------- .../src/components/DialogTagCreate/index.tsx | 14 ++++---------- .../src/components/DialogTagEdit/index.tsx | 19 +++++++------------ 6 files changed, 32 insertions(+), 46 deletions(-) create mode 100644 .changeset/media-inline-dialog-footers.md diff --git a/.changeset/media-inline-dialog-footers.md b/.changeset/media-inline-dialog-footers.md new file mode 100644 index 0000000000..39a2c96d69 --- /dev/null +++ b/.changeset/media-inline-dialog-footers.md @@ -0,0 +1,5 @@ +--- +"sanity-plugin-media": patch +--- + +Fix the Save button staying disabled when editing filename, title, alt text, or description in the asset details dialog. The form reset that runs when the dialog opens (and when the asset or tag is updated elsewhere) now keeps fields registered (`keepFieldsRef`), which is required now that the plugin is built with React Compiler. Dialog footers are also no longer declared as inline components, so the footer no longer remounts whenever form state changes. diff --git a/plugins/sanity-plugin-media/src/components/DialogAssetEdit/index.tsx b/plugins/sanity-plugin-media/src/components/DialogAssetEdit/index.tsx index dd9b67e9eb..7ffab25255 100644 --- a/plugins/sanity-plugin-media/src/components/DialogAssetEdit/index.tsx +++ b/plugins/sanity-plugin-media/src/components/DialogAssetEdit/index.tsx @@ -331,10 +331,15 @@ const DialogAssetEdit = (props: Props) => { } }, [getValues, lastRemovedTagIds, setValue]) - // Reset react-hook-form local state on mount and every time the asset has been updated elsewhere + // Reset react-hook-form local state on mount and every time the asset has been updated elsewhere. + // `keepFieldsRef` is required with React Compiler: a plain `reset()` empties react-hook-form's + // internal field registry and relies on the `register()` calls re-running on the next render to + // re-register every field. The compiler memoizes the registered field JSX (keyed on the stable + // `register` reference), so without this option the fields stay unregistered after reset and + // edits to string fields no longer mark the form dirty (Save stays disabled). useEffect(() => { if (assetUpdatedPrev.current !== assetItem?.asset._updatedAt) { - reset(generateDefaultValues(assetItem?.asset)) + reset(generateDefaultValues(assetItem?.asset), {keepFieldsRef: true}) } assetUpdatedPrev.current = assetItem?.asset._updatedAt }, [assetItem?.asset, generateDefaultValues, reset]) diff --git a/plugins/sanity-plugin-media/src/components/DialogConfirm/index.tsx b/plugins/sanity-plugin-media/src/components/DialogConfirm/index.tsx index c600a6b7a3..77c6478f86 100644 --- a/plugins/sanity-plugin-media/src/components/DialogConfirm/index.tsx +++ b/plugins/sanity-plugin-media/src/components/DialogConfirm/index.tsx @@ -37,7 +37,9 @@ const DialogConfirm = (props: Props) => { handleClose() } - const Footer = () => ( + // Plain JSX, not inline components: a new component identity per render remounts the + // footer/header subtrees whenever captured values change (see DialogAssetEdit for details). + const footer = (