@beignet/ react-hook-form (英语)
运行时间 : Beignet需要Node.js 22.12或更新. Bun是可选的.
Beignet 的后置钩形式集成
[叹息] Beignet是实验性的α软件. 这个
0.0.x包行为早期 评估,且在框架结算时,API可能在发布之间发生变化.
这个软件包提供自动形式验证,并使用一个 合同体计划,或产生合同投入的单独形式计划. 所选择的窗体图案必须有对象形状的输入. 它与任何 标准Schema 图书馆 (佐德,瓦利博特,ArkType等.
安装
npm install @beignet/react-hook-form @beignet/core react-hook-form@^7.55.0 @hookform/resolvers@^7.0.1 react
支助 @hookform/resolvers v5, v6, 和 v7 带有反应钩形式 7.55.0 或更新
v7. 现有 v5 和 v6 安装仍然支持; 新启动器使用 v7.
Beignet的标准Schema集成对这些升级不需要修改.
解析提交、原始模式、计划默认和变换以及可选
解析器设置保留其行为和推论. 自定义解析器跟随
移民:
v6 需要效应 v4
页:1 effectTsResolver,以及
v7 移动类型框到 typebox 1.x 运算
改为 @sinclair/typebox,带有新的错误类型和消息.
类型脚本要求
此包需要 TypeScript 5.0 或更高才能进行适当的类型推断.
代理技能
这个包装了一种用于编码的 TanStack Intental 技能:
@beignet/react-hook-form#forms加入合同支持表时装入,
反应 Hook Form 设置,标准Schema解析器行为, rootFormError, (中文(简体) ),
反应查询突变, 改变身体计划, 特性组件
边框,或 Beignet app 中的字段/服务器错误映射.
使用量
基本形式
这些例子使用从Beignet起步的Todos合同. 创建接受
title; 完成属于更新合同。 工作形式:
核查步骤 窗体指南。 。 。 .
"use client";
import { createReactHookForm } from "@beignet/react-hook-form";
import { createTodo } from "@/features/todos/contracts";
const rhf = createReactHookForm();
function CreateTodoForm() {
const { useForm } = rhf(createTodo);
const form = useForm({
defaultValues: {
title: "",
},
});
const onSubmit = form.handleSubmit((values) => {
// values is parsed schema output: { title: string }
console.log("Creating todo:", values);
});
return (
<form onSubmit={onSubmit}>
<input
{...form.register("title")}
placeholder="What needs to be done?"
/>
{form.formState.errors.title && (
<p className="error">{form.formState.errors.title.message}</p>
)}
<button type="submit" disabled={form.formState.isSubmitting}>
Create Todo
</button>
</form>
);
}
使用反应查询突变
"use client";
import { createReactHookForm, rootFormError } from "@beignet/react-hook-form";
import { useMutation } from "@tanstack/react-query";
import { rq } from "@/client";
import { createTodo, listTodos } from "@/features/todos/contracts";
const rhf = createReactHookForm();
function CreateTodoForm() {
const { useForm } = rhf(createTodo);
const form = useForm({
defaultValues: { title: "" },
});
const mutation = useMutation(
rq(createTodo).mutationOptions({
invalidates: () => [rq(listTodos).contractFilter()],
onSuccess: () => form.reset(),
onError: (error) => {
form.setError("root", rootFormError(error, "Could not create the todo."));
},
}),
);
const onSubmit = form.handleSubmit((values) => {
form.clearErrors("root");
mutation.mutate({ body: values });
});
return (
<form onSubmit={onSubmit}>
<input {...form.register("title")} placeholder="Title" readOnly={mutation.isPending} />
{form.formState.errors.title && (
<p>{form.formState.errors.title.message}</p>
)}
{form.formState.errors.root && (
<p>{form.formState.errors.root.message}</p>
)}
<button type="submit" disabled={mutation.isPending}>
{mutation.isPending ? "Creating..." : "Create"}
</button>
</form>
);
}
React Hook Form只拥有请求体域. 通过路径参数,查询参数,
头,以及端点调用或突变变量的认证衍生值.
提交失败作为 Beignet 客户端错误返回, 所以地图路由自有目录
错误、框架验证错误、网络故障和合同漂移
结束 rootFormError(...)设置 form.setError("root", ...) 或一个
当您的服务器响应确定一个时, 指定字段 .
无效验证
如果您需要禁用 schema 解析器( 例如, 用于部分形式处理 ) :
const { useForm } = rhf(createTodo);
const form = useForm({
resolverEnabled: false, // Disable schema validation
defaultValues: { title: "" },
});
直接使用合同配置
您可以通过合同构建者或其配置:
import { createReactHookForm } from "@beignet/react-hook-form";
import { createTodo } from "@/features/todos/contracts";
const rhf = createReactHookForm();
// Using ContractBuilder directly
const { useForm } = rhf(createTodo);
// Or using the contract config
const { useForm } = rhf(createTodo.config);
API 参考
createReactHookForm()
创建反应钩式适配器厂.
const rhf = createReactHookForm();
rhf(contract, options?)
默认使用合同正文计划创建适配器。 过 { schema }
绑定一个单独的表单计划,其产出可转让给合同机构
输入。 选定的计划输入必须是带有命名字段的对象。 斯卡尔
和阵列机构需要一种明确的对象输入形式,生成
请求形状。 没有机构的合同被拒绝.
const adapter = rhf(createTodo);
adapter.useForm(props?)
返回反应钩表 useForm 以选定的方案作为解析器.
const form = adapter.useForm({
defaultValues: { title: "" },
resolverEnabled: true, // default
submitValues: "parsed", // default; use "raw" for original input
mode: "onBlur",
});
rootFormError(error, fallback, overrides?)
将一个失败的端点调用或突变错误映射到
form.setError("root", ...) 形状。 环绕 contractErrorMessage 从
@beignet/core/client:非合同错误返回倒置副本,
客户端输入验证失败返回一个通用“ 检查突出显示
字段“消息”和目录代码可以覆盖每个表格的复制.
form.setError(
"root",
rootFormError(error, "Could not update profile.", {
HANDLE_UNAVAILABLE: "That handle is already taken.",
}),
);
类型推论
- 现场(
register, (中文(简体) ),watch, (中文(简体) ),setValue, (中文(简体) ),getValues, (中文(简体) ),defaultValues(中文(简体) ) 使用选定的方案 投入。 。 。 . handleSubmit收到解析图 产出 默认,包括 变换、默认和去除未知密钥。 活地状态不变.submitValues: "raw"验证但返回所提交的原始快照.resolverEnabled: false返回未解析的输入。 需要明确解析模式 生成的解析器。 动态或可选启用的旗帜返回输入/输出联盟 .- 习惯
resolver拥有其输出类型,即使生成被禁用. 它不能与submitValues。 。 。 . - 两种适配器方法都接受转发
UseFormProps和可选的解决者. 缺失的覆盖返回到生成的解析器或未分解的输入 当世代被禁用时。 提交类型包括每个可能的输出 . 一个undefined选项对象用途生成验证和解析输出. - 有条件的配置可选择自定义解析器或生成模式;
提交类型包括每个分支的输出。 无法设置单一配置
两者
resolver财务报告和审定财务报表submitValues如果整个选项可能缺席, 返回属性,例如defaultValues财务报告和审定财务报表mode也可以选择.
重新使用合同计划
对于一个数字值的 API, 使用 z.number() 在合同中配置
数字字段 register("amount", { valueAsNumber: true })。提交已解析
给输入的客户端回调值; 客户端发送未修改的输入
而服务器会独立验证它.
使用 z.coerce.number() 在共同合同中,如果API有意接受
数弦也. 只有在
解析值仍为有效的请求输入,再次解析保存其
意为. z.string().transform(Number) 无法解析其数值输出; 偶数
同类型变换可以再次改变一个值. 使用单独的表单方案
此类UI转换,或原始模式来提交原始合同输入.
绑定单独的表单方案
import { defineContract } from "@beignet/core/contracts";
import { createReactHookForm } from "@beignet/react-hook-form";
import { z } from "zod";
const paymentBody = z.object({
amount: z.number().int().positive(),
currency: z.string().default("USD"),
});
const createPayment = defineContract({ method: "POST", path: "/api/payments" })
.body(paymentBody)
.responses({ 201: z.object({ id: z.string() }) });
const paymentFormSchema = paymentBody.extend({
amount: z.string().trim().regex(/^\d+$/)
.transform(Number)
.pipe(paymentBody.shape.amount),
});
const rhf = createReactHookForm();
const paymentForm = rhf(createPayment, { schema: paymentFormSchema });
const form = paymentForm.useForm({ defaultValues: { amount: "" } });
form.watch("amount"); // string
form.handleSubmit((body) => {
body.amount; // number, matching the request body input
body.currency; // string, including the schema default
});
窗体 schema 拥有窗体验证;适配器不额外运行 合同计划。 从共享验证器中获取,以保持运行时间限制 对齐。 TypeScript 拒绝不兼容的输出形状, 但无法检查每个形状 运行时间规则。 原始或已禁用的文件使用表单的输入并可能 需要绘制地图才能公布合同.
使用 defaultValues 对于默认的 UI 显示。 生成解析器
复制纯对象、阵列和同步验证前的日期,抓取
拥有过一次 类例、文件、函数和其他不透明值
保留其身份;在审定和提交期间保持其永久不变.
自定义解析器拥有其验证和快照行为.
React Hook 窗体包装的选项
formOptions(...) 返回与本地兼容的选项 UseFormProps, (中文(简体) ),
useForm,以及 createFormControl,包括 exactOptionalPropertyTypes
启用 。 输入/输出类型匹配适配器 useForm部分
defaultValues 能够缩小反钩形式本身的推论, 所以供应所有三个
通用术语:
import { useForm } from "react-hook-form";
const form = useForm<
z.input<typeof paymentFormSchema>,
unknown,
z.output<typeof paymentFormSchema>
>(paymentForm.formOptions({ defaultValues: { amount: "" } }));
那个适配器 useForm 自动推断这些。 提交调用值
而不是通过重读可变字段状态 getValues()。 。 。 .
标准Schema 支持
此软件包使用 @hookform/resolvers/standard-schema 解析器, 它与任何标准 Schema 兼容库合作 :
- 佐德语Name - 说吧
z.object({ ... }) - 维利博特 - 说吧
v.object({ ... }) - ArkType 密码 - 说吧
type({ ... })
验证行为
当生成的解析器运行时反应Hook Form控制. 默认
反应 Hook Form 设置, 解析器在提交前验证, 然后
在提交失败后重新验证已更改的字段 。 通过正常的反作用钩表单
选项,例如 mode: "onBlur" 或者说 reValidateMode: "onChange" 当一个表单
需要不同的时间.
验证错误可通过 form.formState.errors数字 :
{form.formState.errors.title && (
<span className="error">
{form.formState.errors.title.message}
</span>
)}
完整实例
import { createReactHookForm } from "@beignet/react-hook-form";
import { useMutation } from "@tanstack/react-query";
import { rq } from "@/client";
import { rootFormError } from "@/client/errors";
import { updateProfile } from "@/features/profile/contracts";
const rhf = createReactHookForm();
function ProfileForm({ profile }) {
const { useForm } = rhf(updateProfile);
const form = useForm({
defaultValues: {
name: profile.name,
email: profile.email,
bio: profile.bio ?? "",
},
});
const mutation = useMutation(
rq(updateProfile).mutationOptions({
onSuccess: () => {
toast.success("Profile updated!");
},
onError: (error) => {
form.setError("root", rootFormError(error, "Could not update the profile."));
},
})
);
const onSubmit = form.handleSubmit((values) => {
form.clearErrors("root");
mutation.mutate({ body: values });
});
const { errors, isDirty, isSubmitting } = form.formState;
return (
<form onSubmit={onSubmit}>
<div>
<label htmlFor="name">Name</label>
<input id="name" {...form.register("name")} />
{errors.name && <span className="error">{errors.name.message}</span>}
</div>
<div>
<label htmlFor="email">Email</label>
<input id="email" type="email" {...form.register("email")} />
{errors.email && <span className="error">{errors.email.message}</span>}
</div>
<div>
<label htmlFor="bio">Bio</label>
<textarea id="bio" {...form.register("bio")} />
{errors.bio && <span className="error">{errors.bio.message}</span>}
</div>
<button type="submit" disabled={!isDirty || isSubmitting}>
{isSubmitting ? "Saving..." : "Save Changes"}
</button>
{errors.root && <span className="error">{errors.root.message}</span>}
</form>
);
}
相关软件包
@beignet/core/contracts- 核心合同定义@beignet/react-query- TanStack 查询整合@beignet/core/client- HTTP 客户端
许可证
麻省理工学院