停止重写此 React 表单 UX 逻辑
Source: Dev.to

隐藏的问题
像 React Hook Form 和 Formik 这样的库在它们擅长的领域表现出色。它们管理表单状态、跟踪脏字段并执行验证。但它们有意地在一件事上止步不前:验证失败后在 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 Hook Form 和 Zod 的示例:
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
初始阶段 — 欢迎反馈
该项目仍在持续演进中。当前的功能集故意保持精简,但仍有扩展空间。
如果你曾自行编写过此类表单 UX 逻辑,欢迎尝试本包。我真诚期待:
- 对 API 设计的反馈
- 新功能的建议
通过 GitHub Issues 提交 bug 报告。
作者:Sayan Paul。

