From bc6ed5a1dd86206ed795c321f598856cb8f0a185 Mon Sep 17 00:00:00 2001 From: DHANUSH RAJA <155062318+DHANUSHRAJA22@users.noreply.github.com> Date: Mon, 25 Aug 2025 23:12:56 +0530 Subject: [PATCH] feat: Add controlled field detection for form reset functionalityAdd controlled field detection for form reset functionalityAdd controlled field detection for form reset functionalityUpdate ReactDOMFormActions.js This commit enhances the form reset functionality in ReactDOMFormActions.js by: 1. Added isControlledField() helper function that detects if a form field is controlled (has value or checked props from React) 2. Added resetFormFieldsAfterAction() function that resets form fields after successful form action with logic to: - Skip resetting controlled fields (preserving React state management) - Reset uncontrolled fields as before 3. Modified requestFormReset() to use the new controlled/uncontrolled detection logic 4. Exported helper functions for potential external use This ensures that controlled components maintain their state while uncontrolled components are properly reset after form actions. --- .../src/shared/ReactDOMFormActions.js | 89 ++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/packages/react-dom-bindings/src/shared/ReactDOMFormActions.js b/packages/react-dom-bindings/src/shared/ReactDOMFormActions.js index 6978f4884506b..e4dffd5423e8b 100644 --- a/packages/react-dom-bindings/src/shared/ReactDOMFormActions.js +++ b/packages/react-dom-bindings/src/shared/ReactDOMFormActions.js @@ -6,10 +6,8 @@ * * @flow */ - import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes'; import type {Awaited} from 'shared/ReactTypes'; - import ReactSharedInternals from 'shared/ReactSharedInternals'; import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals'; @@ -64,6 +62,86 @@ function resolveDispatcher() { return ((dispatcher: any): Dispatcher); } +/** + * Helper function to determine if a form field is controlled + * A field is considered controlled if it has a value or checked prop from React + */ +function isControlledField(element: HTMLElement): boolean { + // Get the React fiber node associated with this DOM element + const fiber = element._reactInternalFiber || element.__reactInternalInstance; + + if (!fiber) { + return false; + } + + // Check for controlled props on the fiber's memoizedProps + const props = fiber.memoizedProps; + if (!props) { + return false; + } + + // For input elements, check for 'value' or 'checked' props + if (element.tagName === 'INPUT') { + const inputType = element.type; + if (inputType === 'checkbox' || inputType === 'radio') { + return props.hasOwnProperty('checked'); + } else { + return props.hasOwnProperty('value'); + } + } + + // For textarea and select elements, check for 'value' prop + if (element.tagName === 'TEXTAREA' || element.tagName === 'SELECT') { + return props.hasOwnProperty('value'); + } + + return false; +} + +/** + * Reset form fields after a successful form action + * Only reset uncontrolled fields, skip controlled fields + */ +function resetFormFieldsAfterAction(form: HTMLFormElement): void { + const formElements = form.elements; + + for (let i = 0; i < formElements.length; i++) { + const element = formElements[i] as HTMLElement; + + // Skip if this is a controlled field + if (isControlledField(element)) { + if (__DEV__) { + console.log('Skipping reset for controlled field:', element); + } + continue; + } + + // Reset uncontrolled fields as before + if (element.tagName === 'INPUT') { + const inputElement = element as HTMLInputElement; + const inputType = inputElement.type; + + if (inputType === 'checkbox' || inputType === 'radio') { + inputElement.checked = inputElement.defaultChecked; + } else if (inputType !== 'submit' && inputType !== 'button' && inputType !== 'reset') { + inputElement.value = inputElement.defaultValue; + } + } else if (element.tagName === 'TEXTAREA') { + const textareaElement = element as HTMLTextAreaElement; + textareaElement.value = textareaElement.defaultValue; + } else if (element.tagName === 'SELECT') { + const selectElement = element as HTMLSelectElement; + selectElement.selectedIndex = selectElement.defaultSelected ? selectElement.selectedIndex : 0; + + // Reset individual options + for (let j = 0; j < selectElement.options.length; j++) { + const option = selectElement.options[j]; + option.selected = option.defaultSelected; + } + } + } +} + export function useFormStatus(): FormStatus { const dispatcher = resolveDispatcher(); return dispatcher.useHostTransitionStatus(); @@ -79,6 +157,13 @@ export function useFormState( } export function requestFormReset(form: HTMLFormElement) { + // Reset form fields with controlled/uncontrolled logic + resetFormFieldsAfterAction(form); + + // Call the original reset functionality ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */ .r(/* requestFormReset */ form); } + +// Export the helper function for potential external use +export {isControlledField, resetFormFieldsAfterAction};