Skip to content

Commit

Permalink
Use FormData submitter parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
jenseng committed May 16, 2024
1 parent 149b917 commit e33216e
Show file tree
Hide file tree
Showing 6 changed files with 27 additions and 77 deletions.
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,8 @@ 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);
// $FlowExpectedError[extra-arg]: flow doesn't know about the 'submitter' parameter yet
const formData = new FormData(form, submitter);
const pendingState: FormStatus = {
pending: true,
data: formData,
Expand Down Expand Up @@ -160,9 +135,8 @@ function extractEvents(
event.preventDefault();

// Dispatch the action and set a pending form status.
const formData = submitter
? createFormDataWithSubmitter(form, submitter)
: new FormData(form);
// $FlowExpectedError[extra-arg]: flow doesn't know about the 'submitter' parameter yet
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 @@ -487,11 +487,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 @@ -506,6 +511,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 @@ -544,9 +555,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 @@ -79,17 +79,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
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

0 comments on commit e33216e

Please sign in to comment.