Repository: react-hook-form/react-hook-form (TypeScript)

Issue:
issue: `isDirty` is not updated after toggling `disabled` in `register()`
### Version Number

7.45.4

### Codesandbox/Expo snack

https://codesandbox.io/s/react-hook-form-disabled-dirty-m0d4xo

### Steps to reproduce

1. Check the "Disable (useForm)" checkbox. Notice the submit button becomes enabled, since the form is now dirty.
2. Uncheck the "Disable (useForm)" checkbox. The form is now theoretically clean, and yet RHF reports the form is dirty (submit button is still enabled).
3. In the "Name" input, append a dummy character and remove it, such that the input contains "initial". Now the form is clean.

### Expected behaviour

The form should not be dirty after step 2.

### What browsers are you seeing the problem on?

Firefox, Chrome, Safari, Edge

### Relevant log output

_No response_

### Code of Conduct

- [X] I agree to follow this project's Code of Conduct


Discussion:
I also have a failing test that reproduces the bug: https://github.com/selimb/react-hook-form/commit/d2a5d9d9d95b2d9175d357e5d2ce7369cdaad879



Patch:
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 25d2357c25f..c66070f4225 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -8,8 +8,8 @@
       "internalConsoleOptions": "neverOpen",
       "disableOptimisticBPs": true,
       "cwd": "${workspaceFolder}",
-      "runtimeExecutable": "yarn",
-      "args": ["test", "--runInBand", "--watchAll=false"]
+      "runtimeExecutable": "pnpm",
+      "args": ["test", "--", "--runInBand", "--watchAll=false"]
     }
   ]
 }
diff --git a/.vscode/settings.json b/.vscode/settings.json
index d9c1983e10f..2fec26d96b4 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -3,7 +3,7 @@
     "source.fixAll.eslint": true
   },
   "typescript.tsdk": "node_modules/typescript/lib",
-  "jest.jestCommandLine": "yarn test",
+  "jest.jestCommandLine": "pnpm test --",
   "jest.autoRun": {
     "watch": false,
     "onSave": "test-file",
diff --git a/src/logic/createFormControl.ts b/src/logic/createFormControl.ts
index 3bb5b230352..5d52b2b7de8 100644
--- a/src/logic/createFormControl.ts
+++ b/src/logic/createFormControl.ts
@@ -949,16 +949,17 @@ export function createFormControl<
     });
     _names.mount.add(name);
 
-    field
-      ? disabledIsDefined &&
-        set(
-          _formValues,
-          name,
-          options.disabled
-            ? undefined
-            : get(_formValues, name, getFieldValue(field._f)),
-        )
-      : updateValidAndValue(name, true, options.value);
+    if (field) {
+      if (disabledIsDefined) {
+        const value = options.disabled
+          ? undefined
+          : get(_formValues, name, getFieldValue(field._f));
+        set(_formValues, name, value);
+        updateTouchAndDirty(name, value, false, false, true);
+      }
+    } else {
+      updateValidAndValue(name, true, options.value);
+    }
 
     return {
       ...(disabledIsDefined ? { disabled: options.disabled } : {}),


Test patch:
diff --git a/src/__tests__/useForm/formState.test.tsx b/src/__tests__/useForm/formState.test.tsx
index 25ece8748a8..db5af907b47 100644
--- a/src/__tests__/useForm/formState.test.tsx
+++ b/src/__tests__/useForm/formState.test.tsx
@@ -667,6 +667,38 @@ describe('formState', () => {
     expect(dirtyFieldsState).toEqual({});
   });
 
+  it('should recompute isDirty after toggling disabled', async () => {
+    let isDirty: null | boolean = null;
+
+    const App = () => {
+      const defaultValues = { name: 'initial', disableName: false };
+      const { formState, register, watch } = useForm({ defaultValues });
+
+      isDirty = formState.isDirty;
+
+      const disableName = watch('disableName', defaultValues.disableName);
+
+      return (
+        <form>
+          <input type="text" {...register('name', { disabled: disableName })} />
+          <input type="checkbox" {...register('disableName')} />
+        </form>
+      );
+    };
+
+    render(<App />);
+
+    const checkbox = screen.getByRole('checkbox');
+
+    fireEvent.click(checkbox);
+
+    expect(isDirty).toBe(true);
+
+    fireEvent.click(checkbox);
+
+    expect(isDirty).toBe(false);
+  });
+
   describe('when delay config is set', () => {
     const message = 'required.';