diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 363048f070c94..11c290d1481cb 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -1068,11 +1068,21 @@ export function canHydrateInstance( if ( enableFormActions && type === 'input' && - (element: any).type === 'hidden' && - anyProps.type !== 'hidden' + (element: any).type === 'hidden' ) { - // Skip past hidden inputs unless that's what we're looking for. This allows us - // embed extra form data in the original form. + if (__DEV__) { + checkAttributeStringCoercion(anyProps.name, 'name'); + } + const name = anyProps.name == null ? null : '' + anyProps.name; + if ( + anyProps.type !== 'hidden' || + element.getAttribute('name') !== name + ) { + // Skip past hidden inputs unless that's what we're looking for. This allows us + // embed extra form data in the original form. + } else { + return element; + } } else { return element; } diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js index 6487b48d03ef0..b13eb90304a34 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js @@ -524,6 +524,7 @@ describe('ReactDOMFizzForm', () => { // @gate enableFormActions it('can provide a custom action on buttons the server for actions', async () => { + const hiddenRef = React.createRef(); const inputRef = React.createRef(); const buttonRef = React.createRef(); let foo; @@ -546,7 +547,7 @@ describe('ReactDOMFizzForm', () => { function App() { return (
- + { ReactDOMClient.hydrateRoot(container, ); }); + expect(hiddenRef.current.name).toBe('foo'); + submit(inputRef.current); expect(foo).toBe('bar'); @@ -598,4 +601,53 @@ describe('ReactDOMFizzForm', () => { expect(foo).toBe('bar'); }); + + // @gate enableFormActions + it('can hydrate hidden fields in the beginning of a form', async () => { + const hiddenRef = React.createRef(); + + let invoked = false; + function action(formData) { + invoked = true; + } + action.$$FORM_ACTION = function (identifierPrefix) { + const extraFields = new FormData(); + extraFields.append(identifierPrefix + 'hello', 'world'); + return { + action: '', + name: identifierPrefix, + method: 'POST', + encType: 'multipart/form-data', + data: extraFields, + }; + }; + function App() { + return ( + + + + + ); + } + + const stream = await ReactDOMServer.renderToReadableStream(); + await readIntoContainer(stream); + + const barField = container.querySelector('[name=bar]'); + + await act(async () => { + ReactDOMClient.hydrateRoot(container, ); + }); + + expect(hiddenRef.current).toBe(barField); + + expect(hiddenRef.current.name).toBe('bar'); + expect(hiddenRef.current.value).toBe('baz'); + + expect(container.querySelectorAll('[name=bar]').length).toBe(1); + + submit(hiddenRef.current.form); + + expect(invoked).toBe(true); + }); });