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>
posted @ 2025-11-12 20:31  丁少华  阅读(13)  评论(0)    收藏  举报