停止重写此 React 表单 UX 逻辑

发布: (2026年3月20日 GMT+8 00:19)
5 分钟阅读
原文: Dev.to

Source: Dev.to

封面图片:停止重写此 React 表单 UX 逻辑

Sayan Paul

隐藏的问题

React Hook FormFormik 这样的库在它们擅长的领域表现出色。它们管理表单状态、跟踪脏字段并执行验证。但它们有意地在一件事上止步不前:验证失败后在 DOM 中会发生什么

这项责任每次都落在你身上。

通常从小事开始:

const firstError = document.querySelector("[aria-invalid='true']");
firstError?.focus();

接着你需要滚动页面。随后又要处理嵌套字段路径。再然后,有人要求在表单顶部提供可访问的错误摘要。

不久之后,你就会拥有一个充满 DOM 查询逻辑的自定义工具文件,这些代码与某个项目紧密耦合,且从未被复用。

Source:

小型可复用解决方案

我多次遇到这种模式,于是把它提取成了一个小型开源包,名为 react-form-ux

思路很简单:为表单提供少量专注的 UX 原语,除此之外不做任何其他事。

当前功能

  • focusFirstError() – 将焦点定位到 DOM 中第一个无效输入框
  • scrollToError() – 平滑滚动到第一个验证错误处
  • getErrorFields() – 返回包含错误的字段名数组(支持嵌套路径)
  • ErrorSummary – 渲染一个可访问、可点击的错误列表

该库是 无 UI 依赖(headless)的。它不提供任何 CSS 或 UI 方案,也不关心你使用的是 React Hook Form、Formik,还是完全自定义的实现。

你只需传入一个 errors 对象,库会处理相应的 DOM 行为。

一目了然,该库可以帮助你:

  • 引导用户定位到第一个验证错误
  • 自动将焦点聚焦到正确的输入框
  • 将长表单滚动到相关字段位置
  • 渲染可访问的错误摘要

演示

提交无效表单时会自动滚动到第一个错误并聚焦该字段。

react-form-ux demo

代码示例

以下是使用 React Hook FormZod 的示例:

import { useForm } from "react-hook-form";
import { useFormUX, ErrorSummary } from "react-form-ux";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";

const schema = z.object({
  email: z.string().email("Invalid email address"),
  password: z.string().min(8, "Must be at least 8 characters"),
});

export function SignupForm() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    resolver: zodResolver(schema),
  });

  const { focusFirstError, scrollToError } = useFormUX({ errors });

  const onSubmit = (data: z.infer<typeof schema>) => console.log(data);

  const onError = () => {
    setTimeout(() => {
      scrollToError();
      focusFirstError();
    }, 100);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit, onError)}>
      <div>
        <label htmlFor="email">Email</label>
        <input id="email" {...register("email")} />
        {errors.email && <p>{errors.email.message}</p>}
      </div>

      <div>
        <label htmlFor="password">Password</label>
        <input id="password" type="password" {...register("password")} />
        {errors.password && <p>{errors.password.message}</p>}
      </div>

      <ErrorSummary errors={errors} />

      <button type="submit">Sign Up</button>
    </form>
  );
}

setTimeout 给 React 留出一点时间在滚动和聚焦之前渲染错误元素。这是唯一需要注意的地方。

为什么会有这个

这个包 不是 用来取代你的表单库。两者的职责是分开的:

  • 表单库 负责验证、状态和数据。
  • react-form-ux 负责验证失败后的用户体验行为。

通过拆分这些关注点,你的表单组件保持简洁,用户也能真正看到出错的地方。

安装

npm install react-form-ux

📦 npm 包
🔗 GitHub 仓库

初始阶段 — 欢迎反馈

该项目仍在持续演进中。当前的功能集故意保持精简,但仍有扩展空间。

如果你曾自行编写过此类表单 UX 逻辑,欢迎尝试本包。我真诚期待:

  • 对 API 设计的反馈
  • 新功能的建议

通过 GitHub Issues 提交 bug 报告。

作者:Sayan Paul

0 浏览
Back to Blog

相关文章

阅读更多 »

公平在行动

🎨 从偏见到平衡 — 前端性别公平的故事 💡 概念 - 打破系统性偏见 - 公平优于平等 - 共同崛起 🚀 Code javascript imp...