Skip to content

Commit

Permalink
Allow hidden inputs as extra nodes that we don't match
Browse files Browse the repository at this point in the history
Really this could be limited to just buttons but I just made it a general
thing.
  • Loading branch information
sebmarkbage committed Apr 28, 2023
1 parent 62feb18 commit b2b497c
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 3 deletions.
25 changes: 22 additions & 3 deletions packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -1047,20 +1047,39 @@ export function canHydrateInstance(
): null | Instance {
while (instance.nodeType === ELEMENT_NODE) {
const element: Element = (instance: any);
const anyProps = (props: any);
if (element.nodeName.toLowerCase() !== type.toLowerCase()) {
if (!inRootOrSingleton || !enableHostSingletons) {
// Usually we error for mismatched tags.
return null;
if (
enableFormActions &&
element.nodeName === 'INPUT' &&
(element: 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 {
return null;
}
}
// In root or singleton parents we skip past mismatched instances.
} else if (!inRootOrSingleton || !enableHostSingletons) {
// Match
return element;
if (
enableFormActions &&
type === 'input' &&
(element: any).type === 'hidden' &&
anyProps.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.
} else {
return element;
}
} else if (isMarkedHoistable(element)) {
// We've already claimed this as a hoistable which isn't hydrated this way so we skip past it.
} else {
// We have an Element with the right type.
const anyProps = (props: any);

// We are going to try to exclude it if we can definitely identify it as a hoisted Node or if
// we can guess that the node is likely hoisted or was inserted by a 3rd party script or browser extension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -695,4 +695,41 @@ describe('ReactDOMServerHydration', () => {
);
}
});

// @gate enableFormActions
it('allows rendering extra hidden inputs in a form', async () => {
const element = document.createElement('div');
element.innerHTML =
'<form>' +
'<input type="hidden" /><input type="hidden" name="a" value="A" />' +
'<input type="hidden" /><input type="submit" name="b" value="B" />' +
'<input type="hidden" /><button name="c" value="C"></button>' +
'<input type="hidden" />' +
'</form>';
const form = element.firstChild;
const ref = React.createRef();
const a = React.createRef();
const b = React.createRef();
const c = React.createRef();
await act(async () => {
ReactDOMClient.hydrateRoot(
element,
<form ref={ref}>
<input type="hidden" name="a" value="A" ref={a} />
<input type="submit" name="b" value="B" ref={b} />
<button name="c" value="C" ref={c} />
</form>,
);
});

// The content should not have been client rendered.
expect(ref.current).toBe(form);

expect(a.current.name).toBe('a');
expect(a.current.value).toBe('A');
expect(b.current.name).toBe('b');
expect(b.current.value).toBe('B');
expect(c.current.name).toBe('c');
expect(c.current.value).toBe('C');
});
});

0 comments on commit b2b497c

Please sign in to comment.