Skip to content

Use FormData submitter parameter #29028

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,30 +45,6 @@ function coerceFormActionProp(
}
}

function createFormDataWithSubmitter(
form: HTMLFormElement,
submitter: HTMLInputElement | HTMLButtonElement,
) {
// The submitter's value should be included in the FormData.
// It should be in the document order in the form.
// Since the FormData constructor invokes the formdata event it also
// needs to be available before that happens so after construction it's too
// late. We use a temporary fake node for the duration of this event.
// TODO: FormData takes a second argument that it's the submitter but this
// is fairly new so not all browsers support it yet. Switch to that technique
// when available.
const temp = submitter.ownerDocument.createElement('input');
temp.name = submitter.name;
temp.value = submitter.value;
if (form.id) {
temp.setAttribute('form', form.id);
}
(submitter.parentNode: any).insertBefore(temp, submitter);
const formData = new FormData(form);
(temp.parentNode: any).removeChild(temp);
return formData;
}

/**
* This plugin invokes action functions on forms, inputs and buttons if
* the form doesn't prevent default.
Expand Down Expand Up @@ -129,9 +105,7 @@ function extractEvents(
if (didCurrentEventScheduleTransition()) {
// We're going to set the pending form status, but because the submission
// was prevented, we should not fire the action function.
const formData = submitter
? createFormDataWithSubmitter(form, submitter)
: new FormData(form);
const formData = new FormData(form, submitter);
const pendingState: FormStatus = {
pending: true,
data: formData,
Expand Down Expand Up @@ -160,9 +134,7 @@ function extractEvents(
event.preventDefault();

// Dispatch the action and set a pending form status.
const formData = submitter
? createFormDataWithSubmitter(form, submitter)
: new FormData(form);
const formData = new FormData(form, submitter);
const pendingState: FormStatus = {
pending: true,
data: formData,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -172,25 +172,7 @@ export function listenToFormSubmissionsForReplaying() {
event.preventDefault();

// Take a snapshot of the FormData at the time of the event.
let formData;
if (formDataSubmitter) {
// The submitter's value should be included in the FormData.
// It should be in the document order in the form.
// Since the FormData constructor invokes the formdata event it also
// needs to be available before that happens so after construction it's too
// late. We use a temporary fake node for the duration of this event.
// TODO: FormData takes a second argument that it's the submitter but this
// is fairly new so not all browsers support it yet. Switch to that technique
// when available.
const temp = document.createElement('input');
temp.name = formDataSubmitter.name;
temp.value = formDataSubmitter.value;
formDataSubmitter.parentNode.insertBefore(temp, formDataSubmitter);
formData = new FormData(form);
temp.parentNode.removeChild(temp);
} else {
formData = new FormData(form);
}
const formData = new FormData(form, formDataSubmitter);

// Queue for replaying later. This field could potentially be shared with multiple
// Reacts on the same page since each one will preventDefault for the next one.
Expand Down
24 changes: 19 additions & 5 deletions packages/react-dom/src/__tests__/ReactDOMForm-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ global.IS_REACT_ACT_ENVIRONMENT = true;
// Our current version of JSDOM doesn't implement the event dispatching
// so we polyfill it.
const NativeFormData = global.FormData;
const FormDataPolyfill = function FormData(form) {
const formData = new NativeFormData(form);
const FormDataPolyfill = function FormData(form, submitter) {
const formData = new NativeFormData(form, submitter);
const formDataEvent = new Event('formdata', {
bubbles: true,
cancelable: false,
Expand Down Expand Up @@ -491,11 +491,16 @@ describe('ReactDOMForm', () => {
const inputRef = React.createRef();
const buttonRef = React.createRef();
const outsideButtonRef = React.createRef();
const imageButtonRef = React.createRef();
let button;
let buttonX;
let buttonY;
let title;

function action(formData) {
button = formData.get('button');
buttonX = formData.get('button.x');
buttonY = formData.get('button.y');
title = formData.get('title');
}

Expand All @@ -510,6 +515,12 @@ describe('ReactDOMForm', () => {
<button name="button" value="edit" ref={buttonRef}>
Edit
</button>
<input
type="image"
name="button"
href="/some/image.png"
ref={imageButtonRef}
/>
</form>
<form id="form" action={action}>
<input type="text" name="title" defaultValue="hello" />
Expand Down Expand Up @@ -548,9 +559,12 @@ describe('ReactDOMForm', () => {
expect(button).toBe('outside');
expect(title).toBe('hello');

// Ensure that the type field got correctly restored
expect(inputRef.current.getAttribute('type')).toBe('submit');
expect(buttonRef.current.getAttribute('type')).toBe(null);
await submit(imageButtonRef.current);

expect(button).toBe(null);
expect(buttonX).toBe('0');
expect(buttonY).toBe('0');
expect(title).toBe('hello');
});

it('excludes the submitter name when the submitter is a function action', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,17 +118,7 @@ describe('ReactFlightDOMForm', () => {
const method = (submitter && submitter.formMethod) || form.method;
const encType = (submitter && submitter.formEnctype) || form.enctype;
if (method === 'post' && encType === 'multipart/form-data') {
let formData;
if (submitter) {
const temp = document.createElement('input');
temp.name = submitter.name;
temp.value = submitter.value;
submitter.parentNode.insertBefore(temp, submitter);
formData = new FormData(form);
temp.parentNode.removeChild(temp);
} else {
formData = new FormData(form);
}
const formData = new FormData(form, submitter);
return POST(formData);
}
throw new Error('Navigate to: ' + action);
Expand Down
Loading