Skip to content

Commit 7a054c9

Browse files
committed
Compare name when hydrating hidden fields to filter out extra form action fields
1 parent 6fc3333 commit 7a054c9

File tree

2 files changed

+65
-5
lines changed

2 files changed

+65
-5
lines changed

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,11 +1068,21 @@ export function canHydrateInstance(
10681068
if (
10691069
enableFormActions &&
10701070
type === 'input' &&
1071-
(element: any).type === 'hidden' &&
1072-
anyProps.type !== 'hidden'
1071+
(element: any).type === 'hidden'
10731072
) {
1074-
// Skip past hidden inputs unless that's what we're looking for. This allows us
1075-
// embed extra form data in the original form.
1073+
if (__DEV__) {
1074+
checkAttributeStringCoercion(anyProps.name, 'name');
1075+
}
1076+
const name = anyProps.name == null ? null : '' + anyProps.name;
1077+
if (
1078+
anyProps.type !== 'hidden' ||
1079+
element.getAttribute('name') !== name
1080+
) {
1081+
// Skip past hidden inputs unless that's what we're looking for. This allows us
1082+
// embed extra form data in the original form.
1083+
} else {
1084+
return element;
1085+
}
10761086
} else {
10771087
return element;
10781088
}

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

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,7 @@ describe('ReactDOMFizzForm', () => {
524524

525525
// @gate enableFormActions
526526
it('can provide a custom action on buttons the server for actions', async () => {
527+
const hiddenRef = React.createRef();
527528
const inputRef = React.createRef();
528529
const buttonRef = React.createRef();
529530
let foo;
@@ -546,7 +547,7 @@ describe('ReactDOMFizzForm', () => {
546547
function App() {
547548
return (
548549
<form>
549-
<input type="hidden" name="foo" value="bar" />
550+
<input type="hidden" name="foo" value="bar" ref={hiddenRef} />
550551
<input
551552
type="submit"
552553
formAction={action}
@@ -588,6 +589,8 @@ describe('ReactDOMFizzForm', () => {
588589
ReactDOMClient.hydrateRoot(container, <App />);
589590
});
590591

592+
expect(hiddenRef.current.name).toBe('foo');
593+
591594
submit(inputRef.current);
592595

593596
expect(foo).toBe('bar');
@@ -598,4 +601,51 @@ describe('ReactDOMFizzForm', () => {
598601

599602
expect(foo).toBe('bar');
600603
});
604+
605+
// @gate enableFormActions
606+
it('can hydrate hidden fields in the beginning of a form', async () => {
607+
const hiddenRef = React.createRef();
608+
609+
let invoked = false;
610+
function action(formData) {
611+
invoked = true;
612+
}
613+
action.$$FORM_ACTION = function (identifierPrefix) {
614+
const extraFields = new FormData();
615+
extraFields.append(identifierPrefix + 'hello', 'world');
616+
return {
617+
action: '',
618+
name: identifierPrefix,
619+
method: 'POST',
620+
encType: 'multipart/form-data',
621+
data: extraFields,
622+
};
623+
};
624+
function App() {
625+
return (
626+
<form action={action}>
627+
<input type="hidden" name="bar" defaultValue="baz" ref={hiddenRef} />
628+
<input type="text" name="foo" defaultValue="bar" />
629+
</form>
630+
);
631+
}
632+
633+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
634+
await readIntoContainer(stream);
635+
636+
const barField = container.querySelector('[name=bar]');
637+
638+
await act(async () => {
639+
ReactDOMClient.hydrateRoot(container, <App />);
640+
});
641+
642+
expect(hiddenRef.current).toBe(barField);
643+
644+
expect(hiddenRef.current.name).toBe('bar');
645+
expect(hiddenRef.current.value).toBe('baz');
646+
647+
expect(container.querySelectorAll('[name=bar]').length).toBe(1);
648+
649+
submit(hiddenRef.current.form);
650+
});
601651
});

0 commit comments

Comments
 (0)