跳到主要内容

form action event plugin

一、作用

二、提取事件

备注
/**
* This plugin invokes action functions on forms, inputs and buttons if
* the form doesn't prevent default.
* 如果表单没有阻止默认行为,这个插件会在表单、输入框和按钮上调用操作函数。
*/
function extractEvents(
dispatchQueue: DispatchQueue,
domEventName: DOMEventName,
maybeTargetInst: null | Fiber,
nativeEvent: AnyNativeEvent,
nativeEventTarget: null | EventTarget,
eventSystemFlags: EventSystemFlags,
targetContainer: EventTarget,
) {
if (domEventName !== 'submit') {
return;
}
if (!maybeTargetInst || maybeTargetInst.stateNode !== nativeEventTarget) {
// If we're inside a parent root that itself is a parent of this root, then
// its deepest target won't be the actual form that's being submitted.
// 如果我们处于一个父根节点中,而该父根节点本身又是这个根节点的父节点,那么
// 它的最深目标不会是实际被提交的表单。
return;
}
const formInst = maybeTargetInst;
const form: HTMLFormElement = nativeEventTarget as any;
let action = coerceFormActionProp(
(getFiberCurrentPropsFromNode(form) as any).action,
);
let submitter: null | void | HTMLInputElement | HTMLButtonElement = (
nativeEvent as any
).submitter;
let submitterAction;
if (submitter) {
const submitterProps = getFiberCurrentPropsFromNode(submitter);
submitterAction = submitterProps
? coerceFormActionProp((submitterProps as any).formAction)
: // The built-in Flow type is ?string, wider than the spec
// 内置的 Flow 类型是 ?string,比规范更宽泛
(submitter.getAttribute('formAction') as any as string | null);
if (submitterAction !== null) {
// The submitter overrides the form action.
// 提交者会覆盖表单的 action。
action = submitterAction;
// If the action is a function, we don't want to pass its name
// value to the FormData since it's controlled by the server.
// 如果动作是一个函数,我们不希望将它的名称或值传递给 FormData,因为它由
// 服务器控制。
submitter = null;
}
}

const event = new SyntheticEvent(
'action',
'action',
null,
nativeEvent,
nativeEventTarget,
);

function submitForm() {
if (nativeEvent.defaultPrevented) {
// An earlier event prevented form submission. If a transition update was
// also scheduled, we should trigger a pending form status — even if
// no action function was provided.
// 之前的事件阻止了表格提交。如果同时安排了过渡更新,我们也应该触发待处理表单
// 状态——即使没有提供任何操作函数。
if (didCurrentEventScheduleTransition()) {
// We're going to set the pending form status, but because the submission
// was prevented, we should not fire the action function.
// 我们将设置待处理表单的状态,但由于提交被阻止,我们不应触发 action 函数。
const formData = new FormData(form, submitter);
const pendingState: FormStatus = {
pending: true,
data: formData,
method: form.method,
action: action,
};
if (__DEV__) {
Object.freeze(pendingState);
}
startHostTransition(
formInst,
pendingState,
// Pass `null` as the action
// TODO: Consider splitting up startHostTransition into two separate
// functions, one that sets the form status and one that invokes
// the action.
//
// 将 `null` 作为动作传递
// 待办事项:考虑将 startHostTransition 拆分为两个独立的函数,
// 一个用于设置表单状态,另一个用于调用动作。
null,
formData,
);
} else {
// No earlier event scheduled a transition. Exit without setting a
// pending form status.
// 没有早期事件安排过转换。退出时不设置挂起的表单状态。
}
} else if (typeof action === 'function') {
// A form action was provided. Prevent native navigation.
// 已提供表单操作。阻止原生导航。
event.preventDefault();

// Dispatch the action and set a pending form status.
// 分发动作并设置表单为待处理状态。
const formData = new FormData(form, submitter);
const pendingState: FormStatus = {
pending: true,
data: formData,
method: form.method,
action: action,
};
if (__DEV__) {
Object.freeze(pendingState);
}
startHostTransition(formInst, pendingState, action, formData);
} else {
// No earlier event prevented the default submission, and no action was
// provided. Exit without setting a pending form status.
//
// 没有早期事件阻止默认提交,也没有提供任何操作。退出,不设置待处理表单状态。
}
}

dispatchQueue.push({
event,
listeners: [
{
instance: null,
listener: submitForm,
currentTarget: form,
},
],
});
}

export { extractEvents };

三、分发表单重放操作

备注
export function dispatchReplayedFormAction(
formInst: Fiber,
form: HTMLFormElement,
action: FormData => void | Promise<void>,
formData: FormData,
): void {
const pendingState: FormStatus = {
pending: true,
data: formData,
method: form.method,
action: action,
};
if (__DEV__) {
Object.freeze(pendingState);
}
startHostTransition(formInst, pendingState, action, formData);
}

四、工具

1. 强制表单操作属性

备注
function coerceFormActionProp(
actionProp: mixed,
): string | (FormData => void | Promise<void>) | null {
// This should match the logic in ReactDOMComponent
// 这应该与 ReactDOMComponent 中的逻辑匹配
if (
actionProp == null ||
typeof actionProp === 'symbol' ||
typeof actionProp === 'boolean'
) {
return null;
} else if (typeof actionProp === 'function') {
return (actionProp: any);
} else {
if (__DEV__) {
checkAttributeStringCoercion(actionProp, 'action');
}
return (sanitizeURL(
enableTrustedTypesIntegration ? actionProp : '' + (actionProp: any),
): any);
}
}