Rx Form State
Edit page
开始
教程
API
useFormState使用方式方法参数表单状态管理器对象 formState获取表单状态获取表单域状态更新表单状态更新表单域状态表单域配置FormStateContextFieldFieldArrayFormValueMonitoruseFormStateContextuseFormSelectuseFormSubmittinguseFielduseFieldStateuseFieldErroruseFieldValueuseFieldToucheduseFieldArrayRelyRule

useFormState

useFormState是用来创建表单状态管理器对象的自定义 React Hook。通过表单状态管理器对象,我们可以管理与表单相关的状态,包括:

  • 表单值
  • 表单校验状态
  • 表单提交状态

引用:

import { useFormState } from '@sinoui/rx-form-state';

使用方式

注意:因为useFormState是 React Hook,所以必须在 React 函数组件中使用。

import { useFormState } from '@sinoui/rx-form-state';
function FormDemo() {
const initialValue = {}; // 表单默认值
const options = {}; // 创建表单状态管理器的配置
const formState = useFormState(initialValue, options);
}

通过useFormState()产生的formState往往会与FormStateContext结合在一起使用,将formState通过 React 上下文的方式共享到子组件:

import React from 'react';
import { useFormState, FormStateContext, Field } from '@sinoui/rx-form-state';
function FormDemo() {
const initialValue = {}; // 表单默认值
const options = {}; // 创建表单状态管理器的配置
const formState = useFormState(initialValue, options);
return (
<FormStateContext.Provider value={formState}>
<form>
<label>
姓名:
<Field as="input" name="userName" required />
</label>
<label>
年龄:
<Field as="input" name="age" required />
</label>
</form>
</FormStateContext.Provider>
);
}

看一下演示效果:

方法参数

useFormState方法声明如下:

useFormState<T>(initialValues: T = {}, options?: FormStateOptions<T>): FormState;

方法的第一个参数initialValues是初始的表单值,可以不指定,默认为{}。方法的第二个参数options是表单状态管理器的配置,有如下配置选项:

配置项类型描述
validate`(value:any,values: T) => FormStateErrorsundefined`
onSubmit`(values: T, formState: FormState) => Promise<any>void`
relysRelyRule<T>[]指定表单域值关联逻辑。
enableReinitializeboolean是否监听initialValues值变更,如果发生变更,则重新应用新的表单初始值。默认为true,表示启用。

relys是用来指定表单域值关联计算的规则,例如:

const relys = [
// 规则1:A = B + C
[
'B',
'C',
(draft) => {
draft.A = draft.B + draft.C;
},
],
// 规则2:E = D
[
'D',
(draft) => {
draft.E = draft.D;
},
],
// 规则3:F = A * E
[
'A',
'E',
(draft) => {
draft.F = draft.A * draft.E;
},
],
];

也可以通过 addRelyRule 动态添加值关联规则,如下所示:

const formState = useFormState();
useEffect(() => {
const remove = formState.addRelyRule(['B', 'C'], (draft) => {
draft.A = draft.B + draft.C;
});
return remove; // 返回移除校验规则的函数
}, [formState]);

表单状态管理器对象formState

useFormState()返回formState表单状态管理器对象。通过formState,可以获取和更新表单状态。

获取表单状态

formState有以下属性可用来获取不同的表单状态:

属性名类型描述
valuesT表单值。
errorsFormStateErrors表单同步验证错误状态。
isTouchedFormStateTouched表单域被操作状态。
asyncErrorsFormStateErrors表单异步校验错误状态。
isPendingFormStatePending执行异步校验的过程状态。
isSubmittingboolean表单提交中状态。
isFormPendingboolean表单是否有进行中的异步校验。
isValidboolean表单验证是否通过。

通过这些属性即可获取到表单值、校验错误等状态。

如果你的组件关注表单状态的变更,也就是说当成组件状态使用,那么需要用到下面提供的获取表单状态的 React Hooks:

hook类型描述
useFormSubmitting(formState?: FormState) => void获取表单提交中状态的 hook。在表单提交时特别有用。
useFormSelect<T, M>(formState: FormState<T>, selector: (FormStateModel: FormStateModel<T>) => M): M 或者 <T, M>(selector: (FormStateModel: FormStateModel<T>) => M): M提取表单状态的 hook。

下面通过两个例子来看一看如何使用这两个 hooks。

例子 1:在表单中使用的提交按钮

在表单中使用的提交按钮,我们定义为SubmitButton组件。在组件内部可以获取到formState上下文,所以无需将formState传递给上面介绍的两个 hook。如下所示:

function SubmitButton() {
const isSubmitting = useFormSubmitting();
const submitEnabled = useFormSelect(
(formStateModel) =>
!formStateModel.isFormPending &&
formStateModel.isValid &&
!formStateModel.isSubmitting,
);
return (
<button type="submit" disabled={!submitEnabled}>
{isSubmitting ? '正在提交表单...' : '提交'}
</button>
);
}

使用方式类似如:

const formState = useFormState();
<Form formState={formState}>
<SubmitButton />
</Form>;

例子 2:在表单外部使用的提交按钮

在表单外部使用提交按钮,那么这个按钮无法从上下文中获取到formState,则需要通过属性传递给SubmitButton。代码如下:

function SubmitButton({ formState }: { formState: FormState }) {
const isSubmitting = useFormSubmitting(formState);
const submitEnabled = useFormSelect(
formState,
(formStateModel) =>
!formStateModel.isFormPending &&
formStateModel.isValid &&
!formStateModel.isSubmitting,
);
return (
<button type="submit" disabled={!submitEnabled} onClick={formState.submit}>
{isSubmitting ? '正在提交表单...' : '提交'}
</button>
);
}

使用方式类似如:

const formState = useFormState();
<div>
<Form formState={formState}></Form>
<SubmitButton formState={formState} />
</div>;

获取表单域状态

formState提供了两个方法,用来直接获取表单域状态的:

方法类型描述
getFieldState(fieldName: string): FieldStateModel获取一个表单域状态的方法。

获取到的状态有:

interface FieldStateModel {
/**
* 表单域名称
*/
name: string;
/**
* 表单域值
*/
value: any;
/**
* 表单错误
*/
error?: string | null;
/**
* 表单异步错误
*/
asyncError?: string | null;
/**
* 被操作状态
*/
isTouched: boolean;
/**
* 异步校验过程状态
*/
isPending: boolean;
}

我们还可以通过以下自定义 hook,在组件内获取表单域状态:

更新表单状态

formState提供了一些方法,用来更新表单状态:

方法类型描述
updateState(producer: (draft: FormStateModel<T>) => void): FormStateModel<T>更新表单状态。
setValues(values: T) => void更新表单值。
setInitialValues(initialValues: T) => void更新表单初始值。
validate() => boolean验证表单。返回验证结果。如果有校验错误,则返回false,否则返回true
setErrors(errors: FormStateErrors) => void设置表单校验错误
setTouched(touched: FormStateTouched) => void设置所有表单域的点击状态
setAsyncErrors(asyncErrors: FormStateErrors) => void设置异步校验错误
setPending(isPending: FormStatePending): void设置表单异步校验的过程状态
reset(defaultValues?: T): void重置表单
submit(event?: React.FormEvent<HTMLFormElement>): Promise<any>提交表单
setSubmitting(submiting: boolean): void设置提交中状态

特别说明一下updateState()方法。此方法可以设置表单状态的任何地方,采用的是immer的方法更新表单状态,例如:

formState.updateState((draft) => {
draft.values.A = '1';
draft.touched.A = true;
draft.errors.A = '必须大于2';
});

更新表单域状态

formState提供了一些直接操作表单域状态的方法:

方法类型描述
setFieldState(fieldName: string, producer: (draft: FieldStateModel) => void): FieldStateModel设置表单域状态
setFieldValue(fieldName: string, value: any): void设置表单域的值。
validateField(fieldName: string): void校验表单域
validateFields(...fieldNames: string[]): Promise<boolean>同时验证多个表单域
setFieldTouched(fieldName: string, isTouched?: boolean): void设置表单域的被操作状态
setFieldError(fieldName: string, error?: string): void设置表单域错误
setFieldPending(fieldName: string, isPending: boolean): void设置表单域的异步校验过程状态
setFieldAsyncError(fieldName: string, asyncError?: string): void设置表单域的异步错误
blur(fieldName: string): void处理表单域失去焦点事件

除了这些方法,还可以使用useField来获取一些更新表单域的方法。

表单域配置

通过表单域配置,可以为表单域指定校验、异步校验、值关联规则。formState提供了更新表单域配置的方法:

方法类型描述
addField(field: FieldConfig): void添加表单域配置
removeField(fieldName: string): void删除表单域配置

表单域配置类型FielConfig如下:

export interface FieldConfig {
/**
* 表单域名称
*/
name: string;
/**
* 表单域校验方法
*
* @param {*} value 表单域值
* @param {*} values 表单值
* @returns {(string | undefined | null)}
*/
validate(value: any, values: any): string | undefined | null;
/**
* 表单域异步校验方法
*
* @param {*} value 表单域值
* @param {*} values 表单值
*
* @returns {(string | undefined | null | Promise<string | undefined>} 返回校验结果
*/
asyncValidate?: (
value: any,
values: any,
) => Promise<string | undefined> | undefined;
/**
* 关联字段名
*/
relyFields?: string[];
/**
* 值关联计算方法
*/
relyFn?: (values: any) => any;
}