mirror of
https://github.com/facebook/react.git
synced 2026-02-22 03:42:05 +00:00
Use FormData submitter parameter (#29028)
This commit is contained in:
@@ -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.
|
||||
@@ -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,
|
||||
@@ -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,
|
||||
|
||||
@@ -14,4 +14,4 @@ export const completeBoundaryWithStyles =
|
||||
export const completeSegment =
|
||||
'$RS=function(a,b){a=document.getElementById(a);b=document.getElementById(b);for(a.parentNode.removeChild(a);a.firstChild;)b.parentNode.insertBefore(a.firstChild,b);b.parentNode.removeChild(b)};';
|
||||
export const formReplaying =
|
||||
'addEventListener("submit",function(a){if(!a.defaultPrevented){var c=a.target,d=a.submitter,e=c.action,b=d;if(d){var f=d.getAttribute("formAction");null!=f&&(e=f,b=null)}"javascript:throw new Error(\'React form unexpectedly submitted.\')"===e&&(a.preventDefault(),b?(a=document.createElement("input"),a.name=b.name,a.value=b.value,b.parentNode.insertBefore(a,b),b=new FormData(c),a.parentNode.removeChild(a)):b=new FormData(c),a=c.ownerDocument||c,(a.$$reactFormReplay=a.$$reactFormReplay||[]).push(c,d,b))}});';
|
||||
'addEventListener("submit",function(a){if(!a.defaultPrevented){var b=a.target,d=a.submitter,c=b.action,e=d;if(d){var f=d.getAttribute("formAction");null!=f&&(c=f,e=null)}"javascript:throw new Error(\'React form unexpectedly submitted.\')"===c&&(a.preventDefault(),a=new FormData(b,e),c=b.ownerDocument||b,(c.$$reactFormReplay=c.$$reactFormReplay||[]).push(b,d,a))}});';
|
||||
|
||||
@@ -634,25 +634,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.
|
||||
|
||||
@@ -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,
|
||||
@@ -489,11 +489,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');
|
||||
}
|
||||
|
||||
@@ -508,6 +513,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" />
|
||||
@@ -546,9 +557,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 () => {
|
||||
|
||||
@@ -121,17 +121,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);
|
||||
|
||||
Reference in New Issue
Block a user