From a4aceafc63a4a54b507ab60d530b25d9ff189024 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Mon, 11 Sep 2023 18:22:37 -0400 Subject: [PATCH] Fix: Skip hidden inputs before text instance (#27358) Found a hydration bug that happens when you pass a Server Action to `formAction` and the next node is a text instance. The HTML generated by Fizz is something like this: ```html ``` Fiber is supposed to skip over the extra hidden inputs, but it doesn't handle this correctly if the next expected node isn't a host instance. In this case, it's a text instance. Not sure if the proper fix is to change the HTML that is generated, or to change the hydration logic, but in this PR I've done the latter. --- .../src/client/ReactFiberConfigDOM.js | 10 +++++++++- .../ReactServerRenderingHydration-test.js | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index dafc4ff17eb22..96a8e93f84256 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -1216,7 +1216,15 @@ export function canHydrateTextInstance( if (text === '') return null; while (instance.nodeType !== TEXT_NODE) { - if (!inRootOrSingleton || !enableHostSingletons) { + if ( + enableFormActions && + instance.nodeType === ELEMENT_NODE && + instance.nodeName === 'INPUT' && + (instance: any).type === 'hidden' + ) { + // If we have extra hidden inputs, we don't mismatch. This allows us to + // embed extra form data in the original form. + } else if (!inRootOrSingleton || !enableHostSingletons) { return null; } const nextInstance = getNextHydratableSibling(instance); diff --git a/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js b/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js index 8dfe93b9b534a..f574b16040b68 100644 --- a/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js +++ b/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js @@ -732,4 +732,23 @@ describe('ReactDOMServerHydration', () => { expect(c.current.name).toBe('c'); expect(c.current.value).toBe('C'); }); + + // @gate enableFormActions + it('allows rendering extra hidden inputs immediately before a text instance', async () => { + const element = document.createElement('div'); + element.innerHTML = + ''; + const button = element.firstChild; + const ref = React.createRef(); + const extraText = 'me'; + + await act(() => { + ReactDOMClient.hydrateRoot( + element, + , + ); + }); + + expect(ref.current).toBe(button); + }); });