写在前面
本文中提及的 use
开头的函数,都出自与我的 ComposeHooks 项目,它提供了一系列 React Hooks 风格的状态封装函数,可以帮你更好的使用 Compose,无需关心复杂的状态管理,专注于业务与UI组件。
这是系列文章的第10篇,前文:
- 在Compose中使用useRequest轻松管理网络请求
- 在Compose中使用状态提升?我提升个P...Provider
- 在Compose中父组件如何调用子组件的函数?
- 在Compose中方便的使用MVI思想?试试useReducer!
- 在Compose中像使用redux一样轻松管理全局状态
- 在Compose中轻松使用异步dispatch管理全局状态
- 在Jetpack Compose中管理网络请求竟然如此简单!
- 在Jetpack Compose中优雅的使用防抖、节流
- 在 Jetpack Compose 中扩展 useRequest 实现自定义数据处理、异常回滚
表单验证的痛点
在开发中,表单验证是一个非常常见的需求,但在Compose中实现表单验证却不那么简单。传统的做法通常是:
- 为每个表单项创建单独的状态
- 为每个表单项编写验证逻辑
- 手动跟踪整个表单的验证状态
- 在提交时再次验证所有字段
这种方式不仅代码冗长,而且容易出错,特别是在表单项较多的情况下。如果你曾经尝试过在Compose中实现一个复杂的表单,你一定深有体会。
那么,有没有更简单、更优雅的方式来处理表单验证呢?答案是肯定的!今天我要介绍的是 ComposeHooks 中的Form.useForm
钩子,它可以帮助你轻松实现表单验证。
使用 Form.useForm
进行表单验证
基本用法
首先,让我们看一个简单的例子:
kotlin
@Composable
fun SimpleFormExample() {
val form = Form.useForm()
Surface {
Form(form) {
FormItem<String>(name = "name") { (state, validate, msgs) ->
var string by state
Row {
Text(text = "Name:")
OutlinedTextField(value = string ?: "", onValueChange = { string = it })
}
}
// 提交按钮
Row {
Button(onClick = {
if (form.isValidated()) {
val values = form.getAllFields()
// 处理表单提交
println(values)
}
}) {
Text("Submit")
}
}
}
}
}
这个例子展示了Form.useForm
的基本用法:
- 使用
Form.useForm()
创建一个表单实例 - 使用 Headless 组件
Form
包裹整个表单 - 使用 Headless 组件
FormItem
定义表单项,通过name
属性指定表单项的名称 - 在
FormItem
作用域中可以使用state
状态、validate
校验结果、msgs
校验器提示信息列表
添加验证规则
FormItem
组件支持添加多种验证规则,例如:
kotlin
FormItem<String>(
name = "email", // 表单项名称
Email(), // 验证是否为有效的邮箱格式
Required() // 验证是否为必填项
) { (state, validate, msgs) ->
var string by state
Row {
Text(text = "Email:")
Column {
OutlinedTextField(value = string ?: "", onValueChange = { string = it })
// 显示验证错误信息
Text(text = "$validate ${msgs.joinToString("、")}")
}
}
}
内置验证规则
ComposeHooks提供了多种内置的验证规则:
Required()
- 必填项验证Email()
- 邮箱格式验证Mobile()
- 手机号格式验证Phone()
- 电话号码格式验证Regex
- 正则表达式验证
自定义验证规则
如果内置的验证规则不满足你的需求,你还可以创建自定义的验证规则:
kotlin
FormItem<String>(
name = "id",
object : CustomValidator("身份证号码格式错误", {
!it.asBoolean() || (it is String && it.matches(Regex(CHINA_ID_REGEX)))
}) {}
) { (state, validate, msgs) ->
var string by state
// UI代码...
}
表单状态管理
设置表单值
你可以使用setFieldsValue
方法一次性设置多个表单项的值:
kotlin
useMount {
form.setFieldsValue(
"name" to "default",
"mobile" to "111"
)
}
或者使用setFieldValue
设置单个表单项的值:
kotlin
TButton(text = "Set Age to 5") {
form.setFieldValue("age" to 5)
}
重置表单
你可以使用resetFields
方法重置表单:
kotlin
TButton(text = "reset") {
formInstance.resetFields()
}
也可以在重置的同时设置新的值:
kotlin
TButton(text = "reset with values") {
formInstance.resetFields(
"name" to "Junerver",
"age" to 5,
"mobile" to "13566667777",
"email" to "[email protected]"
)
}
监听表单项变化
你可以使用Form.useWatch
来监听表单项的变化,并将它的值作为一个 State
状态:
kotlin
val form = Form.useForm()
val name by Form.useWatch<String>(fieldName = "name", formInstance = form)
// 在UI中使用
Text(text = "Current name: $name")
获取表单验证状态
你可以使用_isValidated()
方法获取表单的验证状态:
kotlin
val canSubmit by form._isValidated()
TButton(text = "submit", enabled = canSubmit) {
// 提交表单
}
在子组件中获取表单实例
当组件位于 Form
作用域下时,在子组件中可以通过 Form.useFormInstance()
获取表单实例
kotlin
val formInstance: FormInstance = Form.useFormInstance()
完整示例
下面是一个更完整的表单示例,包含多种类型的表单项和验证规则:
kotlin
@Composable
fun UseFormExample() {
val form = Form.useForm()
useMount {
form.setFieldsValue(
"name" to "default",
"mobile" to "111"
)
}
// 通过 Form.useWatch 可以监听表单项的内容到一个 State
val name by Form.useWatch<String>(fieldName = "name", formInstance = form)
Surface {
ScrollColumn {
Form(form) {
FormItem<String>(name = "name") { (state, validate, msgs) ->
var string by state
ItemRow(title = "name") {
OutlinedTextField(value = string ?: "", onValueChange = { string = it })
}
}
Spacer(modifier = Modifier.height(18.dp))
FormItem<Int>(name = "age", Required()) { (state, validate, msgs) ->
var age by state
ItemRow(title = "* age") {
Row {
TButton(text = "1", enabled = age != 1) {
// 通过 setFieldValue 可以为表单项设置值
form.setFieldValue("age" to 1)
}
TButton(text = "3", enabled = age != 3) {
form.setFieldValue("age" to 3)
}
TButton(text = "5", enabled = age != 5) {
form.setFieldValue("age" to 5)
}
TButton(text = "null", enabled = age != null) {
form.setFieldValue("age" to null)
}
Text(text = "$validate ${msgs.joinToString("、")}")
}
}
}
// 更多表单项...
// 提交按钮
Sub()
}
Text(text = "by use `Form.useWatch(fieldName,formInstance)`can watch a field\nname: $name")
}
}
}
@Composable
private fun FormScope.Sub() {
// 你可以在子组件中使用 Form.useFormInstance() 获取父组件中的 FormInstance
val formInstance: FormInstance = Form.useFormInstance()
// 获得表单是否校验成功的 **状态**
val canSubmit by formInstance._isValidated()
Row {
TButton(text = "submit", enabled = canSubmit) {
println(
formInstance
.getAllFields()
.toString() + "\nisValidated :" + formInstance.isValidated()
)
}
TButton(text = "reset") {
formInstance.resetFields()
}
TButton(text = "reset with values") {
formInstance.resetFields(
"name" to "Junerver",
"age" to 5,
"mobile" to "13566667777",
"email" to "[email protected]"
)
}
}
}
写在最后
useForm
的工作原理是基于状态管理和验证规则的组合。每个FormItem
都有自己的状态和验证规则,在 FormItem
作用域中使用其提供的状态,当修改状态时,会通过useEffect
触发验证,并更新验证结果。
表单实例(FormInstance
)会跟踪所有表单项的状态和验证结果,并提供方法来获取和操作这些状态。
使用 ComposeHooks 中的useForm
钩子,我们可以轻松地在Jetpack Compose中实现表单验证:
- 无需手动管理每个表单项的状态
- 内置多种常用的验证规则
- 支持自定义验证规则
- 提供丰富的API来操作表单状态
- 自动跟踪整个表单的验证状态
这种方式不仅减少了代码量,还提高了代码的可读性和可维护性。如果你正在使用Jetpack Compose开发表单,不妨试试useForm
钩子,它会让你的表单验证变得更加简单和优雅。
探索更多
项目开源地址:junerver/ComposeHooks
MavenCentral:hooks
kotlin
implementation("xyz.junerver.compose:hooks2:2.1.0")
欢迎使用、勘误、pr、star。