Skyroc Admin Docs
Shared

@skyroc/form

类型安全的 React 表单 primitive,路径级精确订阅 + StandardSchema 校验

概览

包名@skyroc/form
目录packages/primitives/filed-form
版本0.0.2
运行时依赖@radix-ui/react-slot@skyroc/type-utils@skyroc/utils
peerreact、react-dom(^16.8 – ^19)
子入口../react
测试✅ 5 个
READMEREADME.md + README.zh.md

@skyroc/form 是一个类型安全的 React 表单库。设计核心是把表单数据形状(Values)作为泛型贯穿所有 API,让字段路径不是 string 而是 Values 结构推导出的合法字面量联合,配合 ChangeTag bitmask 实现精确订阅、避免无关字段触发重渲染。

适用场景

  • 字段路径 / 值类型必须类型安全
  • 大型表单需要精确订阅(避免重渲染整张表)
  • 想用 Zod / Yup / Valibot 等 StandardSchema 兼容库做校验
  • 需要动态数组、ComputedField、Undo/Redo

核心设计

1. 泛型 Values 贯穿全链路

type Profile = { user: { name: string; age: number }; tags: string[] };

const form = useForm<Profile>();
form.setFieldValue('user.name', 'Alice');   // ✅ 类型安全
form.setFieldValue('user.xxx', 'Alice');    // ❌ 编译报错(user.xxx 不存在)
form.setFieldValue('user.age', 'Alice');    // ❌ 编译报错(age 不是 string)

2. 路径类型由 @skyroc/type-utils 驱动

  • FieldProps.name: AllPathsKeys<Values>
  • useWatch(name: T)PathToDeepType<Values, T>
  • List / useArrayField 使用 ArrayKeys<Values> / ArrayElementValue<Values, K>
  • ValuesOptions.arrayOp(name) 的 insert/remove 参数与数组元素类型绑定

3. ChangeTag bitmask 精确订阅

import { useWatch, useSelector } from '@skyroc/form';

// 只在 user.name 变化时重渲染
const name = useWatch('user.name');

// 自定义选择器 + 精确依赖
const summary = useSelector(
  values => `${values.user.name} - ${values.user.age}`,
  { deps: ['user.name', 'user.age'] }
);

4. StandardSchema 集成

import { z } from 'zod';

const schema = z.object({
  email: z.string().email(),
  age: z.number().int().min(18)
});

<Form schema={schema}>
  <Field name="email"><Input /></Field>
  <Field name="age"><Input type="number" /></Field>
</Form>;

Schema 通过 ~standard.validate 接口接入,兼容 Zod / Yup / Valibot / ArkType 等。

主入口导出

组件

导出作用
Form表单容器,提供 FormInstance context
Field字段绑定(自动处理 value / onChange / onBlur)
List动态数组字段(增删改、key 管理)
ComputedField派生字段(基于其它字段计算)

Hooks

Hook用途
useForm<Values>()创建 form 实例(或从外部传入)
`useWatch(namenames)`
useSelector(selector, { deps })自定义选择器 + 依赖路径
useFieldState(name)字段 meta(touched / dirty / validating)
useFieldError(names)错误对象(推导形状)
useArrayField(name)数组字段操作(push / remove / insert / move)
useEffectField(name, effect)字段变化副作用
useUndoRedo()撤销 / 重做

类型

FormInstanceFormPropsFieldPropsListPropsComputedFieldPropsValidateErrorEntityRuleMetaSubscribeMaskOptionsActionValidateMessagesAllPathsKeysFieldElement 等。

子入口

子路径内容
.form-core 类型 + 全部 React API
./react仅 React 层(组件 + hooks)

目录结构

src/
├── index.ts
├── form-core/                  # 框架无关核心
│   ├── createStore.ts          # FormStore 状态引擎
│   ├── event.ts                # ChangeTag bitmask 订阅
│   ├── middleware.ts           # Action / Middleware 管道
│   ├── types.ts
│   ├── validate.ts
│   ├── validation.ts
│   └── resolver/
│       ├── resolver.ts         # StandardSchemaV1 / 自定义 schema
│       ├── standard.ts
│       └── utils.ts
└── react/
    ├── components/
    │   ├── Form.tsx
    │   ├── Field.tsx
    │   ├── List.tsx
    │   └── ComputedField.tsx
    └── hooks/
        ├── FieldContext.ts     # FormInstance 实现 / 类型核心
        ├── useForm.ts
        ├── useWatch.ts
        ├── useSelector.ts
        ├── useFieldState.ts
        ├── useFieldError.ts
        ├── useFieldArray.ts
        ├── useFieldEffect.ts
        └── useUndoRedo.ts

基础示例

import { Form, Field, useForm } from '@skyroc/form';

interface Login {
  email: string;
  password: string;
}

const LoginForm = () => {
  const form = useForm<Login>();

  function onSubmit(values: Login) {
    console.log(values);
  }

  return (
    <Form form={form} onFinish={onSubmit}>
      <Field name="email">
        <input type="email" />
      </Field>
      <Field name="password">
        <input type="password" />
      </Field>
      <button onClick={() => form.submit()}>登录</button>
    </Form>
  );
};

动态数组

import { List, useArrayField } from '@skyroc/form';

<List name="tags">
  {({ fields, add, remove }) => (
    <>
      {fields.map((field, i) => (
        <Field key={field.key} name={`tags.${i}`}>
          <input />
        </Field>
      ))}
      <button onClick={() => add('')}>+</button>
    </>
  )}
</List>;

form-core 与 React 层

form-core/框架无关的状态引擎:

  • FormStore:字段注册、值、meta、校验、数组操作、submit 生命周期
  • ChangeTag bitmask:把"值/校验状态/数组结构"变化标记为不同位,订阅时按位匹配
  • Middleware:Action 拦截器,schema resolver 也是 middleware

react/ 层把 form-core 桥接到 React:

  • Form 创建 store 并通过 context 传递
  • FielduseSyncExternalStore 订阅特定路径
  • 各 hook 包装 store 提供 React 友好的 API

理论上 form-core 可以脱离 React 复用(如 React Native、Solid),但当前 ./react 是唯一公开子入口。

在 web-ui 中的使用

@skyroc/web-uiForm preset 组件基于本包封装,自动获得:

  • antd 风格的 label / error 视觉
  • 与 Radix Slot 集成的 Control 绑定
  • Tailwind variant 自动应用
import { Form, FormField } from '@skyroc/web-ui';

<Form>
  <FormField name="email" label="邮箱">
    <Input />
  </FormField>
</Form>;

中间件示例

const logger: Middleware = (action, next, store) => {
  console.log('[form]', action.type, action.payload);
  return next(action);
};

useForm({ middlewares: [logger] });

测试

5 个测试文件覆盖:

  • createStore:状态引擎
  • validation:校验逻辑
  • hooks:React hooks 行为
  • react-components:组件交互
  • event-middleware-resolver:事件 + 中间件 + schema 解析

详细文档

  • 中文:packages/primitives/filed-form/README.zh.md
  • 英文:packages/primitives/filed-form/README.md

两份 README 都包含完整 API 参考、进阶示例与设计动机。

On this page