useActionState 阻止表单重置
前言
useActionState是react19新出的api,提供的非受控表单钩子。
但是他有一个缺点,就是每次提交表单后会重置表单状态!
官方美其名曰“遵守原生表现”,但是网上讨伐声音,就足以证明垃圾!
https://github.com/facebook/react/issues/29034
https://github.com/facebook/react/issues/30580
方式1(推荐)
定义重写全局的 form reset,新建 preventGlobalFormReset.ts
/**
* 全局 Patch HTMLFormElement.prototype.reset 方法
* 阻止 React 19 useActionState 自动重置表单
* 参考: https://github.com/facebook/react/issues/29034
*
* 使用方法:在应用入口调用一次 patchFormReset()
*
* 控制选项:
* - 默认:阻止所有表单重置
* - <form data-allow-reset>:允许该表单重置
* - <form data-prevent-reset>:明确阻止该表单重置
* - formRef.current.reset(true):手动调用时传入 true 强制重置
*/
// 保存原始方法的引用
let originalReset: typeof HTMLFormElement.prototype.reset | null = null;
export function patchFormReset(options?: {
/** 默认行为:'prevent' 阻止重置 | 'allow' 允许重置 */
defaultBehavior?: 'prevent' | 'allow';
}) {
if (typeof window === 'undefined') return;
if (originalReset) {
console.warn('[patchFormReset] 已经 patch 过了,跳过');
return;
}
const { defaultBehavior = 'prevent' } = options || {};
// 保存原始的 reset 方法
originalReset = HTMLFormElement.prototype.reset;
// 重写 reset 方法,支持传入参数强制重置
HTMLFormElement.prototype.reset = function (this: HTMLFormElement, force?: boolean) {
// 如果传入 true,强制重置
if (force === true) {
originalReset!.call(this);
return;
}
// 检查表单上的属性
const hasAllowReset = this.hasAttribute('data-allow-reset');
const hasPreventReset = this.hasAttribute('data-prevent-reset');
let shouldReset = defaultBehavior === 'allow';
// 属性优先级高于默认行为
if (hasAllowReset) {
shouldReset = true;
} else if (hasPreventReset) {
shouldReset = false;
}
if (shouldReset) {
originalReset!.call(this);
} else {
// 什么都不做,阻止重置
}
};
}
/**
* 恢复原始的 reset 方法
*/
export function unpatchFormReset() {
if (typeof window === 'undefined') return;
if (!originalReset) {
console.warn('[unpatchFormReset] 没有找到原始的 reset 方法');
return;
}
HTMLFormElement.prototype.reset = originalReset;
originalReset = null;
console.log('[unpatchFormReset] HTMLFormElement.prototype.reset 已恢复');
}
在 main.ts 调用即可
// 全局阻止 React 19 useActionState 自动重置表单
patchFormReset({
defaultBehavior: 'prevent', // 默认阻止重置
});
在组件中,如果想要手动调用重置,仍然可以,只不过加一个强制即可!
formRef.current.reset(true);
方式 2
如果你不想污染全局,可以这么做
订一个一个 hook,usePreventFormReset.ts
import { useEffect, useRef } from 'react';
/**
* 阻止 React 19 useActionState 自动重置表单
* 参考: https://github.com/facebook/react/issues/29034#issuecomment-2843233452
*/
export function usePreventFormReset() {
const formRef = useRef<HTMLFormElement>(null);
useEffect(() => {
const watchReset = (e: Event) => e.preventDefault();
formRef.current?.addEventListener('reset', watchReset);
return () => formRef.current?.removeEventListener('reset', watchReset);
}, []);
return formRef;
}
然后组件内使用即可
// 阻止表单重置
const formRef = usePreventFormReset();
<form action={formAction} ref={formRef}>
// ***
</form>

浙公网安备 33010602011771号