Skip to content

Commit 78c93a8

Browse files
acdliteAndyPengc12
authored andcommitted
Fix: Skip hidden inputs before text instance (facebook#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 <button name="$ACTION_REF_5" formAction="" formEncType="multipart/form-data" formMethod="POST"> <input type="hidden" name="$ACTION_5:0" value="..."/> <input type="hidden" name="$ACTION_5:1" value="..."/> <input type="hidden" name="$ACTION_KEY" value="..."/>Count: <!-- -->0 </button> ``` 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.
1 parent 3634795 commit 78c93a8

File tree

2 files changed

+28
-1
lines changed

2 files changed

+28
-1
lines changed

packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1216,7 +1216,15 @@ export function canHydrateTextInstance(
12161216
if (text === '') return null;
12171217

12181218
while (instance.nodeType !== TEXT_NODE) {
1219-
if (!inRootOrSingleton || !enableHostSingletons) {
1219+
if (
1220+
enableFormActions &&
1221+
instance.nodeType === ELEMENT_NODE &&
1222+
instance.nodeName === 'INPUT' &&
1223+
(instance: any).type === 'hidden'
1224+
) {
1225+
// If we have extra hidden inputs, we don't mismatch. This allows us to
1226+
// embed extra form data in the original form.
1227+
} else if (!inRootOrSingleton || !enableHostSingletons) {
12201228
return null;
12211229
}
12221230
const nextInstance = getNextHydratableSibling(instance);

packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,4 +732,23 @@ describe('ReactDOMServerHydration', () => {
732732
expect(c.current.name).toBe('c');
733733
expect(c.current.value).toBe('C');
734734
});
735+
736+
// @gate enableFormActions
737+
it('allows rendering extra hidden inputs immediately before a text instance', async () => {
738+
const element = document.createElement('div');
739+
element.innerHTML =
740+
'<button><input name="a" value="A" type="hidden" />Click <!-- -->me</button>';
741+
const button = element.firstChild;
742+
const ref = React.createRef();
743+
const extraText = 'me';
744+
745+
await act(() => {
746+
ReactDOMClient.hydrateRoot(
747+
element,
748+
<button ref={ref}>Click {extraText}</button>,
749+
);
750+
});
751+
752+
expect(ref.current).toBe(button);
753+
});
735754
});

0 commit comments

Comments
 (0)