作者 Corbin Crutchley 于 2025 年 3 月 3 日。
我们很高兴地宣布 TanStack Form 的第一个稳定版本现已上线,可用于生产环境!🥳
我们为此次发布支持五种框架:React、Vue、Angular、Solid 和 Lit,以及针对每个框架的多种功能。
$ npm i @tanstack/react-form
# or
$ npm i @tanstack/vue-form
# or
$ npm i @tanstack/angular-form
# or
$ npm i @tanstack/solid-form
# or
$ npm i @tanstack/lit-form
$ npm i @tanstack/react-form
# or
$ npm i @tanstack/vue-form
# or
$ npm i @tanstack/angular-form
# or
$ npm i @tanstack/solid-form
# or
$ npm i @tanstack/lit-form
大约在两年前,我看到 Tanner 在 BlueSky(当时是一个仅限邀请的平台)上发布了他正在开发一个新项目:TanStack Form。
当时,我刚刚为 React 推出了一个名为“HouseForm”的替代表单库,并且我立即被 Tanner 的库带来的一些想法所吸引。
我很有幸参加了 Tanner 很快也要参加的一个黑客马拉松,我们得以花些时间将 HouseForm 的一些 API 集成到项目中。
从那时起,Tanner 将 Form 的大部分控制权交给了我和一群出色的其他维护者。
那么,我们在这段时间里构建了什么?
长时间在“烤箱”中烘烤的一个优点是,TanStack Form 推出时就提供了大量功能,您可以立即使用。
让我们以 React 的适配器为例,介绍其中仅有的几项功能。
就像许多 TanStack 项目一样,Form 彻底改变了“类型安全”表单库的含义。
const form = useForm({
defaultValues: {
name: "",
age: 0
}
});
// TypeScript will correctly tell you that `firstName` is not a valid field
<form.Field name="firstName"/>
// TypeScript will correctly tell you that `name`'s type is a `string`, not a `number`
<form.Field name="name" children={field => <NumberInput value={field.state.value}/>}/>
const form = useForm({
defaultValues: {
name: "",
age: 0
}
});
// TypeScript will correctly tell you that `firstName` is not a valid field
<form.Field name="firstName"/>
// TypeScript will correctly tell you that `name`'s type is a `string`, not a `number`
<form.Field name="name" children={field => <NumberInput value={field.state.value}/>}/>
我们甚至支持对 <form.Field> 中返回的错误进行类型检查。
<form.Field
name="age"
validators={{
onChange: ({ value }) => (value < 12 ? { tooYoung: true } : undefined),
}}
children={(field) => (
<>
<NumberInput value={field.state.value} />
// TypeScript will correctly tell you that `errorMap.onChange` // is an object,
not a string
<p>{field.state.meta.errorMap.onChange}</p>
</>
)}
/>
<form.Field
name="age"
validators={{
onChange: ({ value }) => (value < 12 ? { tooYoung: true } : undefined),
}}
children={(field) => (
<>
<NumberInput value={field.state.value} />
// TypeScript will correctly tell you that `errorMap.onChange` // is an object,
not a string
<p>{field.state.meta.errorMap.onChange}</p>
</>
)}
/>
对了,我们还支持基于字段的验证以及表单验证。可以随意组合它们!
最棒的是什么?您无需传递任何 TypeScript 泛型即可获得此级别的类型安全性。所有内容都从您的运行时用法推断而来。
感谢 Zod、Valibot 和 ArkType 的创建者们的出色工作,我们开箱即支持 Standard Schema;无需其他包。
const userSchema = z.object({
age: z.number().gte(13, 'You must be 13 to make an account'),
})
function App() {
const form = useForm({
defaultValues: {
age: 0,
},
validators: {
onChange: userSchema,
},
})
return (
<div>
<form.Field
name="age"
children={(field) => {
return <>{/* ... */}</>
}}
/>
</div>
)
}
const userSchema = z.object({
age: z.number().gte(13, 'You must be 13 to make an account'),
})
function App() {
const form = useForm({
defaultValues: {
age: 0,
},
validators: {
onChange: userSchema,
},
})
return (
<div>
<form.Field
name="age"
children={(field) => {
return <>{/* ... */}</>
}}
/>
</div>
)
}
但这还不是全部!我们还支持使用异步函数来验证您的代码;并内置了防抖和基于 AbortSignal 的取消功能。
<form.Field
name="age"
asyncDebounceMs={500}
validators={{
onBlurAsync: async ({ value, signal }) => {
const currentAge = await fetchCurrentAgeOnProfile({ signal })
return value < currentAge ? 'You can only increase the age' : undefined
},
}}
/>
<form.Field
name="age"
asyncDebounceMs={500}
validators={{
onBlurAsync: async ({ value, signal }) => {
const currentAge = await fetchCurrentAgeOnProfile({ signal })
return value < currentAge ? 'You can only increase the age' : undefined
},
}}
/>
我们不仅像最初提到的那样支持多种框架;我们还支持多种运行时。无论您使用的是 React Native、NativeScript,甚至是 Next.js 或 TanStack Start 等 SSR 解决方案,我们都能满足您的需求。
事实上,如果您使用 SSR 解决方案,我们甚至可以轻松实现服务器端表单验证。
// app/routes/index.tsx, but can be extracted to any other path
import { createServerValidate, getFormData } from '@tanstack/react-form/start'
import { yourSchemaHere } from '~/constants/forms'
const serverValidate = createServerValidate({
...formOpts,
onServerValidate: yourSchemaHere,
})
export const getFormDataFromServer = createServerFn({ method: 'GET' }).handler(
async () => {
return getFormData()
}
)
// app/routes/index.tsx, but can be extracted to any other path
import { createServerValidate, getFormData } from '@tanstack/react-form/start'
import { yourSchemaHere } from '~/constants/forms'
const serverValidate = createServerValidate({
...formOpts,
onServerValidate: yourSchemaHere,
})
export const getFormDataFromServer = createServerFn({ method: 'GET' }).handler(
async () => {
return getFormData()
}
)
此代码示例省略了一些相关代码以保持简洁。 有关我们 SSR 集成的更多详细信息,请查看我们的文档。
这样一来,相同的验证逻辑就可以在您的前端和后端运行。当用户浏览器禁用 JavaScript 时,您的表单甚至还会显示错误!
然而,我们不会止步不前——我们计划在 v1 稳定后添加新功能。这些功能包括:
以及更多。
我想感谢的人太多了,一旦开始就没完没了。因此,我将分别向我想感谢的几类人致谢。
感谢我们的贡献者:许多人必须齐心协力才能实现这一点。从其他 TanStack 项目的维护者给予我们的指导,到一次性的 PR;所有这些都帮助我们取得了成功。
感谢我们的早期采用者:那些冒风险使用我们的人,并为我们的 API 和功能提供了宝贵的反馈。
感谢报道我们工具的内容创作者:您为我们的项目带来了更多的关注——通过教育和反馈使它变得更好。
感谢更广泛的社区:您使用我们工具的热情极大地激励了团队。
最后,**感谢**您花时间阅读和探索我们最新的工具。❤️