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 🙏

© 2026 – Pkg Stats / Ryan Hefner

taro-form-react

v0.4.1

Published

A form component for Taro 3.x based on React

Readme

Taro 3/4 表单组件

适用于 Taro 3.x/4.x 的简单表单组件,不包含任何输入组件,只提供简单的表单布局/数据收集/联动/校验功能。仅支持 React。

特点

  • 完全响应式。你说用户体验?用户体验有我开发体验重要吗。
  • 强劲的动态表单支持,支持动态增删 Form.Item,暴打 @antmjs/vantui 的表单组件。
  • 表单快照,随时 capture / restore,缓存用户编辑状态不求人。
  • 性能跟屎一样,但是我肯定不会爆炸。

安装

yarn add taro-form-react

使用

基础用法

import Form from "taro-form-react";
// 样式只需要在入口文件引入一次
// 包含 Label 和一些简单布局样式
import "taro-form-react/dist/styles/index.scss";
// 或者使用 css
import "taro-form-react/dist/styles/index.css";

import { Input, Button } from "@tarojs/components";

import type { FormActions } from "taro-form-react";

const Component = () => {
  const formRef = useRef<FormActions>();

  const handleSubmit = () => {
    formRef.current?.submit();
  }

  const handleReset = () => {
    formRef.current?.reset();
  }

  const handleFinish = (values) => {
    console.log(values);
  }

  const handleFinishedFailed = (errors) => {
    console.log(errors);
  }

  return (
    <Form
      // ref 用于触发表单校验、设置值、获取值等操作
      // 具体支持的操作可参考后续文档
      ref={formRef}
      // 丢弃值为 undefined 和 null 的字段
      // 默认为 true,如需保留请设置为 false
      omitNil={true}
      // 提交成功时触发
      onFinish={handleFinish}
      // 提交失败时触发(有表单元素校验失败)
      onFinishFailed={handleFinishedFailed}
    >
      {/* 表单元素示例 */}

      {/* 基础控件 */}
      <Form.Item 
        // Form.Item 必须存在 name 属性
        // 仅支持数组形式,如 ["field", "array", 0, "name"]
        name={["name"]}
        // Form.Item 应该劫持的输入事件,默认为 onChange
        trigger="onInput"
        // Form.Item 应该劫持的 value,默认为 value
        valuePropName="value"
        // 从事件中获取值的方法,默认为 (...args) => args[0]
        getValueFromEvent={e => e.detail.value}
      >
        {/* 必须存在单个子元素,不能是 React.Fragment */}
        {/* Form.Item 会劫持该元素的输入事件 */}
        <Input placeholder="请输入你的姓名" />
      </Form.Item>

      {/* 保留控件 */}
      {/* Form 不会保留不存在对应控件的值 */}
      {/* 即使你调用了 setFieldValue 设置了它 */}
      {/* 如果需要保留某些值,但不需要展示给用户编辑 */}
      {/* 可以使用 Form.Keep */}
      <Form.Keep
        fields={[
          ["field", "array", 0, "gender"],
          ["field", "array", 1, "gender"],
        ]}
      />

      {/* 上下文控件 */}
      {/* 用于实现复杂的表单联动 */}
      {/* Form.Provider 只暴露只读方法(data / getFieldValue 等) */}
      {/* 如果需要写入表单数据,请通过 formRef 调用 */}
      <Form.Provider>
        {({ data }) => {
          return (
            <>
              <Text>你好,{data.name}</Text>
              <Button
                onClick={() => {
                  formRef.current?.setFieldValue(["name"], "");
                }}
              >
                清空姓名
              </Button>
            </>
          );
        }}
      </Form.Provider>
      <Button onClick={handleSubmit}>提交</Button>
      <Button onClick={handleReset}>重置</Button>
    </Form>
  )
}

校验

<Form.Item
  name={["phone"]}
  label="手机号"
  // 设置 required 会自动添加必填校验规则
  // 错误文案默认为 "{label} is required"
  // 可通过 Form 的 getRequiredMessage 自定义
  required
  rules={[
    // 正则校验,值非空时才触发
    { pattern: /^1\d{10}$/, message: "请输入正确的手机号" },
  ]}
  // 在哪些事件上触发校验,默认跟随 trigger
  validateTrigger={["onInput", "onBlur"]}
  trigger="onInput"
  getValueFromEvent={e => e.detail.value}
>
  <Input placeholder="请输入手机号" />
</Form.Item>

<Form.Item
  name={["password"]}
  label="密码"
  rules={[
    // 长度校验
    { min: 6, max: 20, message: "密码长度 6-20 位" },
    // 自定义异步校验
    {
      validator: async (value) => {
        if (value?.includes(" ")) {
          return "密码不能包含空格";
        }
      },
    },
  ]}
  trigger="onInput"
  getValueFromEvent={e => e.detail.value}
>
  <Input password placeholder="请输入密码" />
</Form.Item>

字段联动

<Form
  initialValues={{ type: "personal" }}
>
  <Form.Item
    name={["type"]}
    label="类型"
    trigger="onChange"
  >
    <Radio.Group>
      <Radio value="personal">个人</Radio>
      <Radio value="company">企业</Radio>
    </Radio.Group>
  </Form.Item>

  {/* 通过 Form.Provider 实现条件渲染 */}
  {/* data 是响应式的,type 变化时会自动重渲染 */}
  <Form.Provider>
    {({ data }) => {
      if (data.type === "company") {
        return (
          <Form.Item
            name={["companyName"]}
            label="企业名称"
            required
            trigger="onInput"
            getValueFromEvent={e => e.detail.value}
          >
            <Input placeholder="请输入企业名称" />
          </Form.Item>
        );
      }
      return null;
    }}
  </Form.Provider>

  {/* 使用 dependencies 实现依赖校验 */}
  {/* 当 password 变化时,confirmPassword 会自动重新校验 */}
  <Form.Item
    name={["confirmPassword"]}
    label="确认密码"
    dependencies={[["password"]]}
    rules={[{
      validator: async (value) => {
        const password = formRef.current?.getFieldValue(["password"]);
        if (value && value !== password) {
          return "两次输入的密码不一致";
        }
      },
    }]}
    trigger="onInput"
    getValueFromEvent={e => e.detail.value}
  >
    <Input password placeholder="请再次输入密码" />
  </Form.Item>
</Form>

字段同步

<Form>
  {/* 单源同步:province 变化时,自动将值同步到 city 和 district */}
  <Form.Sync source={["province"]} target={[["city"], ["district"]]} />

  {/* 多字段互相同步:任一字段变化时,其值同步到其余字段 */}
  <Form.Sync fields={[["startDate"], ["endDate"]]} />
</Form>

值转换

<Form.Item
  name={["date"]}
  label="日期"
  // transform 在 getFieldsFormattedValue / submit 时生效
  // 可以将组件值转换为需要的格式
  transform={(value) => value?.format("YYYY-MM-DD")}
>
  <DatePicker />
</Form.Item>

<Form.Item
  name={["address"]}
  label="地址"
  // 当 transformBehavior 为 "merge" 时
  // transform 返回的对象会被 merge 到整个表单数据中
  // 此时 name 会被忽略
  transform={(value) => ({
    province: value?.[0],
    city: value?.[1],
    district: value?.[2],
  })}
>
  <RegionPicker />
</Form.Item>

快照与恢复

import type { FormActions, FormSnapshot } from "taro-form-react";

const EditPage = () => {
  const formRef = useRef<FormActions>();

  // 页面进入时恢复草稿
  useEffect(() => {
    const saved = localStorage.getItem("draft");
    if (saved) {
      formRef.current?.snapshot.restore(JSON.parse(saved));
    }
  }, []);

  // 页面离开时保存草稿
  const handleSaveDraft = () => {
    const snap = formRef.current?.snapshot.capture();
    if (snap) {
      localStorage.setItem("draft", JSON.stringify(snap));
    }
  };

  return (
    <Form ref={formRef} onFinish={handleSubmit}>
      {/* ... */}
      <Button onClick={handleSaveDraft}>保存草稿</Button>
    </Form>
  );
};

在子组件中访问表单

import { useFormContext } from "taro-form-react";

// 在 Form 内部的任意子组件中使用
const SubmitButton = () => {
  // useFormContext 返回完整的表单上下文
  // 包含所有数据和操作方法
  const { data, validateFields, isFieldsTouched } = useFormContext();

  const disabled = !isFieldsTouched();

  return (
    <Button
      disabled={disabled}
      onClick={async () => {
        const errors = await validateFields();
        if (!errors) {
          console.log("提交数据:", data);
        }
      }}
    >
      提交
    </Button>
  );
};

API

NamePath = (string | number)[],所有字段名均使用数组形式,如 ["user", "address", 0, "city"]

Form Props

  • initialValues?: Record<string, any> — 表单初始值
  • omitNil?: boolean(默认 true)— 提交时是否丢弃值为 undefined / null 的字段
  • onFinish?: (values: Record<string, any>) => void — 提交成功(校验通过)回调
  • onFinishFailed?: (errors: { name: NamePath; errors: string[] }[]) => void — 提交失败(校验未通过)回调
  • onFieldsChange?: (changedFields, allFields) => void — 字段注册/卸载时触发
  • onValuesChange?: (changedValues, allValues) => void — 字段值变更时触发

全局配置项

以下配置作用于所有子 Form.Item,也可在每个 Form.Item 上单独覆盖:

  • colon?: boolean — Label 是否显示冒号
  • labelProps?: FormLabelProps — 传递给 Label 的 props
  • layout?: "horizontal" | "vertical" — 布局方向
  • validateFirst?: boolean | "parallel"(默认 false)— true 遇到第一个校验错误即停止;"parallel" 并行执行所有规则
  • showErrors?: boolean — 是否在 Form.Item 下方显示错误文本
  • passthroughErrors?: boolean — 是否将 hasErrorerrors 透传给子组件 props
  • transformBehavior?: "merge" | "replace" — 当 Form.Item 设置了 transform 时,"merge" 将转换结果合并到表单数据,"replace" 直接替换对应字段
  • updateTickLimit?: number(默认 50)— onChange 节流间隔(ms),过小可能导致数据异常
  • getRequiredMessage?: (label: string) => string — 自定义 required 规则的默认错误文案

Form.Item Props

  • name: NamePath必填,字段路径
  • label?: ReactNode(默认 null)— 标签文本,传 null 不渲染标签
  • required?: boolean — 是否必填,会自动添加 required 规则(如果 rules 中没有)
  • rules?: Rule[] — 校验规则,见下方「校验规则」
  • trigger?: string(默认 "onChange")— 劫持子组件的哪个事件来收集值
  • valuePropName?: string(默认 "value")— 劫持子组件的哪个 prop 来注入值
  • getValueFromEvent?: (...args) => any(默认 (...args) => args[0])— 从事件参数中提取值
  • initialValue?: any — 字段初始值(优先级高于 Form 的 initialValues
  • dependencies?: NamePath[] — 依赖字段,依赖变化时自动重新校验
  • noStyle?: boolean — 不渲染外层容器,直接返回子组件
  • hidden?: boolean — 隐藏模式,不渲染子组件但保留字段注册(Form.Keep 内部使用)
  • validateTrigger?: string[](默认 [trigger])— 触发校验的事件
  • transform?: (value: any) => any — 提交时对值进行转换
  • className?: classNames.Argument — 外层容器 className
  • innerClassName?: classNames.Argument — 内层容器 className

Form.Item 要求有且仅有一个子元素(不能是 Fragment),会劫持该元素的输入事件和 value prop。

Form.Keep

保留不需要展示给用户的字段值。Form 默认在字段对应的 Form.Item 卸载后会清除该字段的数据,如果需要保留某些字段的值,使用 Form.Keep。

  • fields: NamePath[] — 需要保留的字段列表
<Form.Keep fields={[["hiddenField"], ["nested", "value"]]} />

Form.Sync

字段同步组件,当源字段值变化时自动同步到目标字段。两种用法:

单源多目标

<Form.Sync source={["province"]} target={[["city"], ["district"]]} />

source 变化时,将其值同步到所有 target 字段。

多字段互相同步

<Form.Sync fields={[["fieldA"], ["fieldB"], ["fieldC"]]} />

fields 中任一字段变化时,其值同步到其余所有字段。

Form.Label

Label 组件,通常由 Form.Item 自动渲染,也可独立使用。

  • label?: ReactNode — 标签文本
  • required?: boolean — 是否显示必填星号
  • size?: "normal" | "small"(默认 "normal"
  • colon?: boolean(默认 true)— 是否显示冒号
  • addonAfter?: ReactNode — 标签后附加内容
  • asteriskTookSpace?: boolean(默认 true)— 星号是否占据空间(不必填时保留占位)
  • className?: classNames.Argument
  • labelClassName?: classNames.Argument
  • colonClassName?: classNames.Argument
  • containerClassName?: classNames.Argument

Form.Provider

Render Props 模式的上下文访问组件,用于实现复杂的表单联动。

<Form.Provider>
  {({ data, getFieldValue, getFieldsValue, getFields, getFieldError, getFieldsFormattedValue, validateFields, isFieldsTouched, submit, reset }) => {
    return <Text>{data.name}</Text>;
  }}
</Form.Provider>

回调参数是只读的表单上下文(不包含 setFieldValuesetFieldsresetFieldssetFieldErrorsetData 等写入方法),加上 submitreset。如果需要在 Provider 内写入表单数据,请通过 formRef 调用对应方法。

FormActions(ref 方法)

通过 ref 获取表单实例,调用以下方法:

const formRef = useRef<FormActions>();
// ...
<Form ref={formRef}>

数据操作

  • setData(data: Record<string, any>) — 直接替换整个表单数据
  • setFieldValue(name: NamePath, value: any) — 设置单个字段的值
  • getFieldValue(name: NamePath): any — 获取单个字段的值
  • getFieldsValue(nameList?: NamePath[]): Record<string, any> — 获取多个字段的值,不传参返回所有
  • getFieldsFormattedValue(nameList?: NamePath[]): Promise<Record<string, any>> — 获取经过 transform 处理后的值

字段操作

  • setFields(fields: { name: NamePath; value?: any; touched?: boolean }[]) — 批量设置字段值和 touched 状态
  • getFields(nameList?: NamePath[]): { name: NamePath; value: any; touched: boolean; errors: string[] }[] — 获取字段完整信息
  • resetFields(nameList?: NamePath[]) — 重置字段到初始值并清除 touched 状态,不传参重置所有

校验

  • validateFields(nameList?: NamePath[]): Promise<void | { name: NamePath; errors: string[] }[]> — 校验字段,通过返回 undefined,失败返回错误数组
  • setFieldError(name: NamePath, errors: string[]) — 手动设置字段错误
  • getFieldError(name: NamePath): string[] — 获取字段错误
  • isFieldsTouched(nameList?: NamePath[], options?: { allTouched?: boolean }): boolean — 检查字段是否被修改过,allTouchedtrue 时要求全部被修改

提交与重置

  • submit(): Promise<Record<string, any> | undefined> — 触发校验并提交,校验通过返回表单值并触发 onFinish,失败返回 undefined 并触发 onFinishFailed
  • reset() — 重置所有字段

快照(snapshot)

通过 formRef.current.snapshot 访问:

  • snapshot.capture(): FormSnapshot — 捕获当前表单完整状态(数据 + touched + errors),返回一个可序列化的对象
  • snapshot.restore(snapshot: FormSnapshot) — 从快照恢复表单状态

capture 返回的对象可以用 JSON.stringify 序列化后存入 localStorage 等持久化存储,也可以直接保存在变量中。restore 会自动处理字段集合不一致的情况(快照中有但当前未注册的字段会在后续注册时自动恢复)。

校验规则(Rule)

Form.Item 的 rules 接受以下四种规则,可混合使用:

必填

{ required: boolean; message: string }

正则

{ pattern: RegExp; message: string }

值非空时校验,空值不触发。

长度/大小

{ min?: number; max?: number; message: string }

number 类型比较数值大小,其他类型比较 .length。值非空时校验。

自定义

{ validator: (value: any) => Promise<void | string> }

返回 string 表示错误信息,返回 void / undefined 表示通过。

useFormContext

在 Form 内部的任意组件中获取表单上下文:

import { useFormContext } from "taro-form-react";

const MyComponent = () => {
  const { data, setFieldValue, getFieldValue } = useFormContext();
  // ...
};

返回值同 FormContextProps,包含所有表单操作方法。必须在 <Form> 内部使用,否则会抛出异常。