1.0.1 • Published 4 years ago

@tingxin_cy/react-super-form v1.0.1

Weekly downloads
-
License
-
Repository
github
Last release
4 years ago

@tingxin_cy/react-super-form

React表单解决方案,专注解决“数据采集”和“数据校验”两大表单核心需求,采用分布式设计从根源解决表格性能问题,同时该方案与UI解耦适用于任何UI框架或者原生UI,支持任何复杂的表单需求。

特性

  • 支持自动化表单校验,内置多种数据类型规则,同时支持自定义的同步校验&异步校验。
  • 支持自动化数据收集,支持按照namePath (例如:'a.b.0.c')进行结构化数据搜集。
  • 表单字段控件支持采用分布式渲染,从而优化表单性能,达到类似非受控组件的效果。
  • 无UI侵入,适用于任务UI框架或者原生UI,一次学习到处使用。
  • 支持多表单实例并存,可并列使用也可嵌套使用。
  • 极简API设计,仅对外输出2个的组件和5个方法。
  • 适用于任何复杂程度的动态表单场景。

安装

yarn add @tingxin_cy/react-super-form -S

or

yarn add @tingxin_cy/react-super-form -S

导入

import ReactSuperForm from '@tingxin_cy/react-super-form';

const { Field, FormSpy } = formInstance; // 核心组件

Class:ReactSuperForm

const formInstance = new ReactSuperForm({
  onValueChange: (name, value, error) => {},
  onErrorsChange: (errors) => {
    this.setState({
      formErrors: errors
    })
  }
});

options

选填 | 参数 | 说明 | 类型 | | ---- | ---- | ---- | | onValueChange | 全局钩子,监听value change事件,适用于一些全局通用的业务逻辑,例如日志发送,实时自动保存数据等 | (name:string, value: string|number|boolean, error:string) => void | | onErrorsChange | 全局钩子,监听全表单errors更新,可用于稍复杂的UI需求,例如需要在组件外部根据某些表单的error信息实现一些UI逻辑,可将errors写入state并进行渲染 | (errors: {key:string:string}|null)=>void |


Component

Field

核心组件,用于增强表单控件

const { Field } = formInstance;
属性
名称说明类型
name表单项Name,表单全局唯一,用于数据收集,支持namePath格式,自动收集格式化数据。string非空
defaultValue表单项默认值,value===undefined时生效,将实现非受控组件的效果,此时可以通过<Field\/>内部注入的onChange方法修改表单项值,此时将采用分布式渲染,性能有优势。string| number| boolean可空
value表单项Value,可通过修改该值实现表单数据的变更,将实现受控组件的效果,此时无法采用<Field \/>内部注入的onChange方法修改表单项值。string| number| boolean可空
rules检验规则,为空时将不对该表单值进行校验,可利用此特性实现单纯的数据收集。Rule[]可空
分布式渲染:
<Field
  name="userName"
  defaultValue=""
  rule={[
    { type: 'string', required: true, message: '用户名不能为空' },
  ]}
>
  {/* 推荐:采用内部注入的参数进行表单项赋值、更新,
      将实现非受控组件的效果,已达到分布式渲染的目的,保证性能 */}
  {({value, onChange, error }) => (
    <div>
      <Input
        value={value}
        onChange={e => onChange(e.target.value)}
      />
      <span>{error}</span>
    </div>
  )}
</Field>

参数注入: | 名称 | 说明 | 类型 | | ---- | ---- | ---- | | value | 表单项Value,用于表单项赋值。 | string|number|boolean | | onChange | 通过该方法进行value更新,自动触发表单校验。 | (value:number|string|boolean) => Promise<{value: string|number|boolean, error:string}> | | error | 错误信息,可用于错误信息展示 | string |


非分布式渲染:
<Field
  name="userName"
  value={this.state.userName}
  rule={[
    { type: 'string', required: true, message: '用户名不能为空' },
  ]}
>
  {/* 不推荐: 不通过注入的onChange方法更新value,
      而是触发外部的setState更新,这将导致页面重绘,容易引发性能问题。 */}
  {({ value, error }) => (
    <div>
      <Input
        value={value}
        onChange={e => this.setState('userName', e.target.value)}
      />
      <span>{error}</span>
    </div>
  )}
</Field>

Rule

表单校验规则,更多信息可查看async-validator。 | 名称 | 说明 | 类型 | | ---- | ---- | ---- | | type? | 数据校验类型 | 'string'|'number'|'integer'| 'float'|'boolean'|'url'| 'email'|'enum',默认值:'string' | | required? | 是否是必填项。 | boolean | | message? | 错误提示信息。 | string | | pattern? | 正则表达式。 | boolean | | min? | 当type为number时,该值限定最小值;当type为string时,该值限定最小长度; | number | | max? | 当type为number时,该值限定最大值;当type为string时,该值限定最大长度; | number | | length? | 当type为number时,限定值大小;当type为string时,限定字符串长度;若length和min、max并存时,length优先 | number | | enum? | 校验value是否符合指定的枚举值 | (number|string|boolean) | | whitespace? | true时,仅包含空格的字符串将被视为空字符串,将触发required错误 | boolean | | validator? | 自定义校验函数,支持同步校验和异步校验 | function (rules, value, callback:(error?: string)=>{}) {} |


FormSpy

监听表单项change,触发区域render,多用于解决联动表单场景

const { FormSpy } = formInstance;
属性
名称说明类型必须
initialValues当前FormSpy表单项初始值,可以按需提供,无需提供全量表单字段{name:string: string|number|boolean}非必须
subscription监听的表单项,当任一表单项value change时,可触发当前FormSpy重新渲染,并获取到最新表单项value,未设置时监控所有表单项{name:string:boolean}非必须

Demo

省市联动场景,当省份切换时,要联动更新城市数据

<>
  <Field
    name="province"
    defaultValue="beijing"
  >
    {({value, onChange}) => (
      <ProvinceSelect 
        value={value} 
        onChange={onChange} 
      />
    )}
  </Field>

  <FormSpy 
    initialValues={
      province="beijing"
    }
    subscription: {
      province: true
    }
  >
    {({values, errors}) => (
      <Field
        name="city"
        defaultValue=""
      >
        {({value, onChange}) => (
          <CitySelect 
            province={values.province}
            value={value} 
            onChange={onChange}
          />
        )}
      </Field>
    )}
  </FormSpy>
</>

参数注入: | 名称 | 说明 | 类型 | | ---- | ---- | ---- | | values | subscription设置的所有表单项值,默认值为initialValues | {key:string: string|number|boolean} | | errors | subscription设置的所有表单项错误信息 | {key:string:string} |


方法

- validateFields():Promise<{errors: {name:string:string}|null, values: any}>

校验所有表单项,返回当前表单收集到的数据和错误信息,多用于表单提交场景

onSubmit() {
  this.formInstance.validateFields().then(({errors, values}:any) => {
    if (!errors) {
      // do submit action
    } else {
      // do something for error
    }
  })
}

- setFieldValue(name: string, value: string|number|boolean):void

修改特定表单项值,主要用于表单联动编辑场景 注意:该方法仅在表单项为非受控组件模式下生效。(value === undefined && defaultValue !== undefined)

<Field
  name="userinfo.workid"
  value={this.state.workid}
  rules={[{ required: true, message: 'Required' }]}
>
  {(props:any) => (
     <Input 
      value={props.value}
      onChange={e => {
        props.onChange(e.target.value);
        this.formInstance.setFieldValue('userinfo.name', `员工-${e.target.value}`);
      }} 
    />
  )}
</Field>

- getValues():Object

实时收集当前状态下表单数据

onButtonClick = () => {
  const values = this.formInstance.getValues();
  console.log(values);
}

- getErrors():{name:string: string};

实时获取当前状态的错误信息 注意:由于校验表单项并绘制表单项error信息较为消耗性能,尤其某些表单采用异步校验时,校验时间略长,所以该方法只返回已经报出表单项错误(手动编辑表单项内容时触发的error),并不会对整体表单进行校验,如需获取表单整体的错误信息请使用validateFields方法

onButtonClick = () => {
  const errors = this.formInstance.getErrors();
  console.log(errors);
}

- reset():void;

表单重置功能,清除所有error,复位至原始value值(Field组件上设置的value属性值)

onButtonClick = () => {
  const values = this.formInstance.reset();
}

Demo

import React from 'react';
import ReactSuperForm from '@tingxin_cy/react-super-form';
import { Input, Radio, Button } from 'antd';

class FormDemo extends React.Component {
  constructor(props) {
    super(props);
    this.formInstance = new ReactSuperForm({
      // 表单全局hook
      onValueChange(name, value, error) {
        console.log(name, value, error);
      }
    });
  }

  onSubmit = () => {
    this.formInstance.validateFields().then(({errors, values}) => {
      console.log(errors);
      console.log(values);

      /* 错误的情况
      {
        userinfo.name: "请填写名称",
        userinfo.gender: "请选择性别",
        syncValue: "内容错误,请检查。",
        asyncValue: "内容错误,请检查。",
        like.0: "请填写第一个爱好",
        like.1: "请填写第二个爱好",
        like.3: "请填写第三个爱好"
      }
      {
        userinfo: {
          name: "",
          gender: ""
        },
        syncValue: "",
        asyncValue: "",
        like: [ "", "", ""]
      }
      */

      /* 正确的情况
      null
      {
        userinfo: {
          name: "张三”,
          gender: "male"
        },
        syncValue: "sync",
        asyncValue: "async",
        like: [ "足球", "篮球", "排球"]
      }
      */
    });
  }

  render() {
    const { Field } = this.formInstance;
    return (
      <div>
        <Field
          name="userinfo.name"
          value=""
          rules={[
            { type: 'string', required: true, message: '请填写名称' }
          ]}
        >
        {(props) => (
          <div>
            <Input
              value={props.value}
              onChange={e => props.onChange(e.target.value)}
            />
            <span>{props.error}</span>
          </div>
        )}
        </Field>

        <Field
          name="userinfo.gender"
          value=""
          rules={[
            { type: 'enum', enum: ['male', 'female'], required: true, message: '请选择性别' }
          ]}
        >
        {(props) => (
          <div>
            <Radio.Group
              onChange={e => props.onChange(e.target.value)}
              value={props.value}
            >
              <Radio value={'male'}>男</Radio>
              <Radio value={'female'}>女</Radio>
            </Radio.Group>
            <span>{props.error}</span>
          </div>
        )}
        </Field>

        {/* 自定义同步校验 */}
        <Field
          name="syncValue"
          value=""
          rules={[
            { validator(rule, value, callback) {
                if (value === 'sync') {
                  callback(); // 校验通过
                } else {
                  callback('内容错误,请检查。')
                }
            } }
          ]}
        >
        {(props) => (
          <div>
            <Input
              value={props.value}
              onChange={e => props.onChange(e.target.value)}
            />
            <span>{props.error}</span>
          </div>
        )}
        </Field>

        {/* 自定义异步校验 */}
        <Field
          name="asyncValue"
          value=""
          rules={[
            { validator(rule, value, callback) {
                Promise.resolve().then(() => {
                  if (value === 'async') {
                    callback(); // 校验通过
                  } else {
                    callback('内容错误,请检查。')
                  }
                })
            } }
          ]}
        >
        {(props) => (
          <div>
            <Input
              value={props.value}
              onChange={e => props.onChange(e.target.value)}
            />
            <span>{props.error}</span>
          </div>
        )}
        </Field>

        <Field
          name="like.0"
          value=""
          rules={[
            { type: 'string', required: true, message: '请填写第一个爱好' }
          ]}
        >
        {(props) => (
          <div>
            <Input
              value={props.value}
              onChange={e => props.onChange(e.target.value)}
            />
            <span>{props.error}</span>
          </div>
        )}
        </Field>

        <Field
          name="like.1"
          value=""
          rules={[
            { type: 'string', required: true, message: '请填写第二个爱好' }
          ]}
        >
        {(props) => (
          <div>
            <Input
              value={props.value}
              onChange={e => props.onChange(e.target.value)}
            />
            <span>{props.error}</span>
          </div>
        )}
        </Field>

        <Field
          name="like.3"
          value=""
          rules={[
            { type: 'string', required: true, message: '请填写第三个爱好' }
          ]}
        >
        {(props) => (
          <div>
            <Input
              value={props.value}
              onChange={e => props.onChange(e.target.value)}
            />
            <span>{props.error}</span>
          </div>
        )}
        </Field>

        <div>
          <Button onClick={this.onSubmit}>提交</Button>
        </div>
      </div>
    );
  }
}

export default FormDemo;
1.0.1

4 years ago

1.0.0

4 years ago