npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@kne/react-form

v3.1.2

Published

用于表单的校验

Readme

react-form

描述

用于表单的校验

安装

npm i --save @kne/react-form

概述

react-form是一个功能强大的React表单库,提供了完整的表单状态管理、验证和提交解决方案。

核心特性

状态管理

  • 使用React的状态管理机制管理表单状态
  • 支持复杂的表单状态结构
  • 提供表单状态的实时更新和访问

事件系统

  • 使用事件发射器模式实现表单内部通信
  • 支持多种表单事件,如提交、验证、重置等
  • 允许自定义事件处理逻辑

字段管理

  • 动态添加、更新和移除表单字段
  • 支持字段级别的状态管理
  • 提供字段值的获取和设置方法

验证系统

  • 支持表单和字段级别的验证
  • 提供内置验证规则
  • 支持自定义验证逻辑
  • 实时验证和提交验证

数据处理

  • 支持表单数据的获取、设置和重置
  • 提供数据转换和格式化功能
  • 支持初始数据设置

分组管理

  • 支持表单字段的分组
  • 允许动态添加和移除分组
  • 支持分组级别的操作和验证

架构设计

组件结构

  • Form:表单的主要容器,负责状态管理和上下文提供
  • Field:表单字段组件,负责单个字段的渲染和交互
  • Group:表单分组组件,用于管理相关字段的集合
  • GroupList:动态分组列表,支持添加和移除分组

核心模块

  • 事件系统:处理表单内部的事件通信
  • 验证系统:处理表单和字段的验证逻辑
  • 拦截器:提供表单操作的拦截和修改能力
  • 任务系统:管理表单的异步任务

使用场景

  • 复杂表单开发
  • 动态表单生成
  • 表单验证和提交
  • 多步骤表单流程
  • 表单数据管理

示例

示例样式

.ant-card {
  border-color: black;
  text-align: center;
  width: 200px;
}

示例代码

  • 基本示例
  • 简单的form到input组件的绑定
  • _ReactForm(@kne/current-lib_react-form)
const { default: ReactForm, useField, useSubmit, useReset } = _ReactForm;

const Input = props => {
  const fieldProps = useField(props);

  return (<div>
    {fieldProps.label}
    <input ref={fieldProps.fieldRef} type="text" value={fieldProps.value || ''} onChange={fieldProps.onChange}
           onBlur={fieldProps.triggerValidate} />
    {fieldProps.errState}
    {fieldProps.errMsg}
  </div>);
};

const SubmitButton = ({ children }) => {
  const { isLoading, onClick } = useSubmit();
  return (<button onClick={onClick}>
    {children}
    {isLoading ? '正在提交中...' : ''}
  </button>);
};

const ResetButton = () => {
  const { onClick } = useReset();
  return <button onClick={onClick}>重置</button>;
};

const BaseExample = () => {
  return <div>
    <ReactForm debug data={{
      name: '哈哈哈'
    }} onSubmit={async (data) => {
      await new Promise((resolve)=>{
        setTimeout(() => {
          resolve();
        },3000);
      });
      console.log('submit:', data);
    }}>
      <Input name="name" label="名称" rule="REQ LEN-0-10" />
      <div>
        <SubmitButton>提交</SubmitButton>
        <ResetButton>重置</ResetButton>
      </div>
    </ReactForm>
  </div>;
};

render(<BaseExample />);
  • openApi
  • 展示openApi的使用
  • _ReactForm(@kne/current-lib_react-form),antd(antd)
const { default: ReactForm, useField, useSubmit, useReset } = _ReactForm;
const { useRef } = React;
const { Button, Space } = antd;

const Input = props => {
  const fieldProps = useField(props);

  return (<div>
    {fieldProps.label}
    <input ref={fieldProps.fieldRef} type="text" value={fieldProps.value || ''} onChange={fieldProps.onChange}
           onBlur={fieldProps.triggerValidate} />
    {fieldProps.errState}
    {fieldProps.errMsg}
  </div>);
};

const SubmitButton = ({ children }) => {
  const { isLoading, onClick } = useSubmit();
  return (<button onClick={onClick}>
    {children}
    {isLoading ? '正在提交中...' : ''}
  </button>);
};

const ResetButton = () => {
  const { onClick } = useReset();
  return <button onClick={onClick}>重置</button>;
};

const BaseExample = () => {
  const formApiRef = useRef();
  return <div>
    <Space>
      <Button onClick={() => {
        console.log(formApiRef.current.data);
      }}>获取表单值</Button>
      <Button onClick={() => {
        formApiRef.current.setField({ name: 'name', value: '哈哈哈' });
      }}>设置字段值</Button>
      <Button onClick={() => {
        formApiRef.current.setFields([{ name: 'name', value: '哈哈哈' }, {
          name: 'name2', value: '哈哈哈2'
        }, { name: 'name3', value: '哈哈哈3' }]);
      }}>设置多个字段值</Button>
      <Button onClick={() => {
        formApiRef.current.setFieldValidate({
          name: 'name2', validate: { status: 2, msg: '我是一个通过api设置的错误' }
        });
      }}>设置校验信息</Button>
    </Space>
    <ReactForm ref={formApiRef}>
      <Input name="name" label="名称" rule="REQ LEN-0-10" />
      <Input name="name2" label="名称2" rule="REQ LEN-0-10" />
      <Input name="name3" label="名称3" rule="REQ LEN-0-10" />
      <div>
        <SubmitButton>提交</SubmitButton>
        <ResetButton>重置</ResetButton>
      </div>
    </ReactForm>
  </div>;
};

render(<BaseExample />);
  • use-form-api
  • 展示useFormApi的使用
  • _ReactForm(@kne/current-lib_react-form),antd(antd)
const { default: ReactForm, useField, useSubmit, useReset, useFormApi } = _ReactForm;
const { useRef } = React;
const { Button, Space } = antd;

const Input = props => {
  const fieldProps = useField(props);
  return (<div>
    {fieldProps.label}
    <input ref={fieldProps.fieldRef} type="text" value={fieldProps.value || ''} onChange={fieldProps.onChange}
           onBlur={fieldProps.triggerValidate} />
    {fieldProps.errState}
    {fieldProps.errMsg}
  </div>);
};

const SubmitButton = ({ children }) => {
  const { isLoading, onClick } = useSubmit();
  return (<button onClick={onClick}>
    {children}
    {isLoading ? '正在提交中...' : ''}
  </button>);
};

const ResetButton = () => {
  const { onClick } = useReset();
  return <button onClick={onClick}>重置</button>;
};

const Options = () => {
  const { openApi: formApi } = useFormApi();

  console.log(formApi);

  return <Space>
    <Button onClick={() => {
      console.log(formApi.getFormData());
    }}>获取表单值</Button>
    <Button onClick={() => {
      formApi.setField({ name: 'name', value: '哈哈哈' });
    }}>设置字段值</Button>
    <Button onClick={() => {
      formApi.setFields([{ name: 'name', value: '哈哈哈' }, {
        name: 'name2', value: '哈哈哈2'
      }, { name: 'name3', value: '哈哈哈3' }]);
    }}>设置多个字段值</Button>
    <Button onClick={() => {
      formApi.setFieldValidate({
        name: 'name2', validate: { status: 2, msg: '我是一个通过api设置的错误' }
      });
    }}>设置校验信息</Button>
  </Space>;
};

const BaseExample = () => {
  const formApiRef = useRef();
  return <div>
    <ReactForm ref={formApiRef}>
      <div><Options /></div>
      <Input name="name" label="名称" rule="REQ LEN-0-10" />
      <Input name="name2" label="名称2" rule="REQ LEN-0-10" />
      <Input name="name3" label="名称3" rule="REQ LEN-0-10" />
      <div>
        <SubmitButton>提交</SubmitButton>
        <ResetButton>重置</ResetButton>
      </div>
    </ReactForm>
  </div>;
};

render(<BaseExample />);
  • group
  • 展示group的使用
  • _ReactForm(@kne/current-lib_react-form),antd(antd)
const { default: ReactForm, useField, useSubmit, useReset, GroupList } = _ReactForm;
const { useRef } = React;

const Input = props => {
  const fieldProps = useField(props);

  return (<div>
    {fieldProps.label}
    <input ref={fieldProps.fieldRef} type="text" value={fieldProps.value || ''} onChange={fieldProps.onChange}
           onBlur={fieldProps.triggerValidate} />
    {fieldProps.errState}
    {fieldProps.errMsg}
  </div>);
};

const SubmitButton = ({ children }) => {
  const { isLoading, onClick } = useSubmit();
  return (<button onClick={onClick}>
    {children}
    {isLoading ? '正在提交中...' : ''}
  </button>);
};

const ResetButton = () => {
  const { onClick } = useReset();
  return <button onClick={onClick}>重置</button>;
};

const BaseExample = () => {
  const ref = useRef();
  const formApiRef = useRef();
  return <div>
    <div>
      <button onClick={() => {
        formApiRef.current.setField({
          name: 'name', groupName: 'group', groupIndex: 0, value: '设置group字段值'
        });
      }}>
        设置group第一项name字段值
      </button>
      <button onClick={() => {
        formApiRef.current.setField({
          name: 'name', groupName: 'group', value: '设置group字段值'
        });
      }}>
        设置group所有项name字段值
      </button>
      <button onClick={() => {
        formApiRef.current.setField({
          groupName: 'group', groupIndex: 0, value: {
            name: '名称', des: '说明'
          }
        });
      }}>
        设置group第一项所有字段值
      </button>
      <button onClick={() => {
        formApiRef.current.setFormData({
          group: [{ name: '第一项' }, { name: '第二项' }, { name: '第三项' }, { name: '第四项' }, { name: '第五项' }]
        });
      }}>设置整个表单的值
      </button>
    </div>
    <ReactForm ref={formApiRef} onSubmit={(data) => {
      console.log('submit:', data);
    }}>
      <div>
        <button onClick={() => {
          ref.current.onAdd();
        }}>倒序添加
        </button>
      </div>
      <GroupList ref={ref} name="group">{({ index, onRemove }) => {
        return <div>
          <div>第{index + 1}项</div>
          <Input name="name" label="名称" rule="REQ LEN-0-10" onChange={() => {
            console.log(index);
          }} />
          <Input name="des" label="描述" rule="LEN-0-10" />
          <GroupList name="inner">{({ index, onRemove }) => {
            return <div style={{
              padding: '10px', background: '#eee'
            }}>
              <div>第{index + 1}项</div>
              <Input name="name" label="名称" rule="LEN-0-10" />
              <Input name="des" label="描述" rule="LEN-0-10" />
              <button onClick={() => {
                onRemove();
              }}>删除子GroupItem
              </button>
            </div>;
          }}</GroupList>
          <button onClick={() => {
            onRemove();
          }}>删除
          </button>
        </div>;
      }}</GroupList>
      <button onClick={() => {
        ref.current.onAdd({ isUnshift: false });
      }}>顺序添加
      </button>
      <div>
        <SubmitButton>提交</SubmitButton>
        <ResetButton>重置</ResetButton>
      </div>
    </ReactForm>
  </div>;
};

render(<BaseExample />);
  • associations
  • 字段关联关系
  • _ReactForm(@kne/current-lib_react-form),antd(antd)
const { default: ReactForm, useField, useSubmit, useReset, GroupList } = _ReactForm;

const { useRef } = React;

const Input = props => {
  const fieldProps = useField(props);

  return (<div>
    {fieldProps.label}
    <input {...fieldProps.associationOptions} ref={fieldProps.fieldRef} type="text" value={fieldProps.value || ''}
           onChange={fieldProps.onChange}
           onBlur={fieldProps.triggerValidate} />
    {fieldProps.errState}
    {fieldProps.errMsg}
  </div>);
};

const SubmitButton = ({ children }) => {
  const { isLoading, onClick } = useSubmit();
  return (<button onClick={onClick}>
    {children}
    {isLoading ? '正在提交中...' : ''}
  </button>);
};

const ResetButton = () => {
  const { onClick } = useReset();
  return <button onClick={onClick}>重置</button>;
};

const BaseExample = () => {
  const ref = useRef(null);
  return <div>
    <ReactForm debug onSubmit={(data) => {
      console.log('submit:', data);
    }}>
      <Input name="name" label="名称" rule="REQ LEN-0-10" />
      <Input name="des" label="描述" rule="LEN-0-10" associations={{
        fields: [{
          name: 'name'
        }], callback: ({ target, origin, openApi }) => {
          openApi.setFieldValue(target, origin.value);
        }
      }} />
      <hr />
      <Input name="familyName" label="姓" rule="REQ LEN-0-10" />
      <Input name="firstName" label="名" rule="REQ LEN-0-10" />
      <Input name="fullName" label="全名" rule="REQ LEN-0-20" associations={{
        fields: [{
          name: 'familyName'
        }, {
          name: 'firstName'
        }], callback: ({ target, openApi }) => {
          const { firstName, familyName } = openApi.getFormData();
          openApi.setFieldValue(target, firstName && firstName ? `${firstName} ${familyName}` : '');
        }
      }} />
      <hr />
      <Input name="money" label="金额" />
      <Input name="ratio" label="比例" />
      <Input name="all" label="总金额" associations={{
        fields: [{
          name: 'money'
        }, {
          name: 'ratio'
        }], callback: ({ target, openApi }) => {
          const { money, ratio } = openApi.getFormData();
          openApi.setFieldValue(target, money && ratio && ratio > 0 ? money / ratio : '');
        }
      }} />
      <hr />
      <div>
        <button onClick={() => {
          ref.current.onAdd();
        }}>添加
        </button>
      </div>
      <GroupList ref={ref} name="group" defaultLength={1}>{({ index, onRemove }) => {
        return <div>
          <div>第{index + 1}项</div>
          <Input name="sum" label="数量" />
          <button onClick={() => {
            onRemove();
          }}>删除
          </button>
        </div>;
      }}</GroupList>
      <Input name="amount" label="总数" associations={{
        fields: [{
          name: 'sum', groupName: 'group'
        }], callback: ({ target, openApi }) => {
          const { group } = openApi.getFormData();
          openApi.setFieldValue(target, (group || []).filter((item) => item.sum > 0).reduce((a, b) => a + parseInt(b.sum), 0));
        }
      }} />
      <div>
        <SubmitButton>提交</SubmitButton>
        <ResetButton>重置</ResetButton>
      </div>
    </ReactForm>
  </div>;
};

render(<BaseExample />);
  • 基本示例
  • 简单的form到input组件的绑定
  • _ReactForm(@kne/current-lib_react-form)
const { default: ReactForm, useField, useSubmit, useReset } = _ReactForm;

const Input = props => {
  const fieldProps = useField(props);

  return (
    <div>
      {fieldProps.label}
      <input ref={fieldProps.fieldRef} type="text" value={fieldProps.value || ''} onChange={fieldProps.onChange} onBlur={fieldProps.triggerValidate} />
      {fieldProps.errState}
      {fieldProps.errMsg}
    </div>
  );
};

const SubmitButton = ({ children }) => {
  const { isLoading, onClick } = useSubmit();
  return (
    <button onClick={onClick}>
      {children}
      {isLoading ? '正在提交中...' : ''}
    </button>
  );
};

const ResetButton = () => {
  const { onClick } = useReset();
  return <button onClick={onClick}>重置</button>;
};

const BaseExample = () => {
  return (
    <div>
      <ReactForm
        debug
        rules={{
          REMOTE_RULE: async value => {
            console.log('>>>>>start');
            await new Promise(resolve => {
              setTimeout(() => {
                resolve();
              }, 1000);
            });
            console.log('>>>>>>>>end');
            return { result: true };
          }
        }}
        data={{
          name: '哈哈哈'
        }}
        onSubmit={async data => {
          console.log('>>>>>>>>>>>>>>>');
          await new Promise(resolve => {
            setTimeout(() => {
              resolve();
            }, 3000);
          });
          console.log('submit:', data);
        }}>
        <Input name="name" label="名称" rule="REQ REMOTE_RULE" />
        <div>
          <SubmitButton>提交</SubmitButton>
          <ResetButton>重置</ResetButton>
        </div>
      </ReactForm>
    </div>
  );
};

render(<BaseExample />);

API

Form组件API

属性

| 属性名 | 说明 | 类型 | 默认值 | |-----|----|----|-----| | initialValues | 表单初始值 | object | {} | | onSubmit | 表单提交回调函数 | function(values, form) | - | | onSubmitFail | 表单提交失败回调函数 | function(errors, form) | - | | onReset | 表单重置回调函数 | function(form) | - | | onValidate | 表单验证回调函数 | function(values, form) | - | | interceptors | 表单拦截器 | object | {} | | children | 表单内容 | React.ReactNode | - |

示例

import { Form, Field } from 'react-form';

const MyForm = () => {
  const handleSubmit = (values) => {
    console.log('Form values:', values);
  };

  return (
    <Form
      initialValues={{ name: 'John', email: '' }}
      onSubmit={handleSubmit}
    >
      <Field name="name" label="Name" />
      <Field name="email" label="Email" />
      <button type="submit">Submit</button>
    </Form>
  );
};

useOpenApi钩子API

useOpenApi钩子提供了一组用于操作表单的方法。

返回值

| 方法名 | 说明 | 参数 | 返回值 | |-----|----|----|-----| | getValues | 获取表单所有字段的值 | - | object | | getValue | 获取指定字段的值 | (name: string) | any | | setValues | 设置表单多个字段的值 | (values: object, runValidate?: boolean) | void | | setValue | 设置指定字段的值 | (name: string, value: any, runValidate?: boolean) | void | | setFields | 设置表单字段的属性 | (fields: array, runValidate?: boolean) | void | | resetFields | 重置表单字段 | - | void | | submit | 提交表单 | - | Promise | | validate | 验证表单 | (names?: string[]) | Promise | | getFieldError | 获取指定字段的错误信息 | (name: string) | string | | getErrors | 获取表单所有字段的错误信息 | - | object | | isFieldTouched | 判断字段是否被用户操作过 | (name: string) | boolean | | isFieldValidating | 判断字段是否正在验证 | (name: string) | boolean | | getFieldsValue | 获取多个字段的值 | (nameList: string[]) | object |

示例

import { Form, useOpenApi } from 'react-form';

const FormWithApi = () => {
  const formApi = useOpenApi();
  
  const handleClick = () => {
    formApi.setValue('name', 'New Name');
    console.log(formApi.getValues());
  };

  return (
    <Form>
      <Field name="name" label="Name" />
      <Field name="email" label="Email" />
      <button type="button" onClick={handleClick}>
        Update Name
      </button>
      <button type="submit">Submit</button>
    </Form>
  );
};

Field组件API

属性

| 属性名 | 说明 | 类型 | 默认值 | |-----|----|----|-----| | name | 字段名称 | string | - | | label | 字段标签 | string | - | | defaultValue | 默认值 | any | - | | rules | 验证规则 | array | [] | | children | 自定义渲染函数 | function(field) | - | | onChange | 值变化回调 | function(value, field) | - | | onBlur | 失焦回调 | function(e, field) | - | | onFocus | 聚焦回调 | function(e, field) | - |

示例

import { Form, Field } from 'react-form';

const MyForm = () => {
  return (
    <Form>
      <Field
        name="username"
        label="Username"
        defaultValue=""
        rules={[
          { required: true, message: 'Please input your username!' },
          { min: 3, message: 'Username must be at least 3 characters' }
        ]}
      >
        {({ value, onChange, error }) => (
          <div>
            <input value={value} onChange={e => onChange(e.target.value)} />
            {error && <div className="error">{error}</div>}
          </div>
        )}
      </Field>
      <button type="submit">Submit</button>
    </Form>
  );
};

Group组件API

属性

| 属性名 | 说明 | 类型 | 默认值 | |-----|----|----|-----| | name | 分组名称 | string | - | | children | 分组内容 | React.ReactNode | - |

GroupList组件属性

| 属性名 | 说明 | 类型 | 默认值 | |-----|----|----|-----| | name | 分组列表名称 | string | - | | children | 分组模板 | function(index) | - | | defaultLength | 初始分组数量 | number | 0 |

示例

import { Form, Field, Group, GroupList } from 'react-form';

const MyForm = () => {
  return (
    <Form>
      <GroupList name="contacts" defaultLength={1}>
        {(index) => (
          <Group name={index}>
            <Field name="name" label="Name" />
            <Field name="phone" label="Phone" />
          </Group>
        )}
      </GroupList>
      <button type="button" onClick={() => formApi.addGroup('contacts')}>
        Add Contact
      </button>
      <button type="submit">Submit</button>
    </Form>
  );
};

表单验证规则API

内置规则

| 规则名 | 说明 | 参数类型 | 示例 | |-----|----|----|-----| | required | 必填字段 | boolean | { required: true, message: '必填字段' } | | min | 最小长度/值 | number | { min: 3, message: '最小长度为3' } | | max | 最大长度/值 | number | { max: 10, message: '最大长度为10' } | | pattern | 正则表达式匹配 | RegExp | { pattern: /^\d+$/, message: '必须为数字' } | | validator | 自定义验证函数 | function | { validator: (value) => value === 'test' ? '' : '验证失败' } |

示例

import { Form, Field } from 'react-form';

const MyForm = () => {
  return (
    <Form>
      <Field
        name="email"
        label="Email"
        rules={[
          { required: true, message: '请输入邮箱' },
          { pattern: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/, message: '邮箱格式不正确' }
        ]}
      />
      <Field
        name="password"
        label="Password"
        rules={[
          { required: true, message: '请输入密码' },
          { min: 6, message: '密码长度不能小于6位' },
          { 
            validator: (value) => {
              if (!/[A-Z]/.test(value)) {
                return '密码必须包含大写字母';
              }
              return '';
            }
          }
        ]}
      />
      <button type="submit">Submit</button>
    </Form>
  );
};

表单拦截器API

拦截器可以在表单操作的不同阶段进行拦截和修改。

可用拦截器

| 拦截器名 | 说明 | 参数 | 返回值 | |-----|----|----|-----| | beforeSubmit | 表单提交前拦截 | (values, form) | values或Promise | | afterSubmit | 表单提交后拦截 | (result, form) | result或Promise | | beforeValidate | 表单验证前拦截 | (values, form) | values或Promise | | afterValidate | 表单验证后拦截 | (errors, form) | errors或Promise | | beforeReset | 表单重置前拦截 | (form) | void或Promise | | afterReset | 表单重置后拦截 | (form) | void或Promise |

示例

import { Form, Field } from 'react-form';

const MyForm = () => {
  const interceptors = {
    beforeSubmit: (values) => {
      console.log('Before submit:', values);
      // 可以修改values
      return {
        ...values,
        timestamp: Date.now()
      };
    },
    afterSubmit: (result) => {
      console.log('After submit:', result);
      return result;
    }
  };

  return (
    <Form
      interceptors={interceptors}
      onSubmit={(values) => console.log('Submit:', values)}
    >
      <Field name="name" label="Name" />
      <Field name="email" label="Email" />
      <button type="submit">Submit</button>
    </Form>
  );
};