时隔半年,历经风雨洗刷,UForm居然活下来了,真是难得!
过去的都是时间,留下来的只有沉淀,半年时间,UForm沉淀了无数Issue与PR,感谢为UForm做过贡献的所有人。
UForm v1官网(临时地址):uform-next.netlify.com/
github仓库地址:github.com/alibaba/ufo…
简要介绍
考虑到还有并不太了解UForm的同学,所以这里先大概介绍一下UForm。
UForm是一个面向中后台复杂业务场景的高性能表单解决方案,它最Base的能力就是高性能,主要原因是它内部的表单渲染从过去对React已知的单向数据流概念变成了点对点通讯模式,保证最小粒度的渲染效率,从而提升了表单整体性能。同时,它还提供了一套强大的副作用管理能力,可以让用户编写各种复杂联动、复杂校验等业务逻辑。还有,它也提供了一套强大的JSON Schema数据驱动的动态表单渲染能力,可以帮助用户解决动态渲染表单的各种需求。简而言之,UForm,非常强大,您想到的,想不到的,在UForm里都有考虑。这也是UForm的核心定位。就是要做一套真正的表单领域型解决方案,而非一个单纯的React组件。
特性
今天,UForm带着广大用户的期待而来,下面先介绍下UForm V1主要有哪些新特性,我们可以从不同维度来讲:
- 项目维度
- 集团多BU共建,包括 alibaba/nopage(noform)作者 @鬼鼠 ,同时也包括集团内部多个BU的前端技术专家参与共建,比如 @黄子毅,目标是为了统一集团对外表单解决方案。
- 从源码到单测的整体Typescript重构,更规范的类型定义,更完备的智能提示
- 更高的单测覆盖率,目前从@uform/core层到@uform/react-schema-renderer层都包含了大量单元测试,保证底层代码绝对稳定。
- 更详细的文档体系
- 增加了很多二次开发(自定义组件开发)教程
- 增加英文文档
- Packages
- @uform/core
- 时间旅行能力支持,整体Form Tree State可观察,可回溯
- 内置immer,精确更新,智能降级,无需关心浏览器版本
- 更加完备的校验引擎
- 悲观校验(validateFirst)能力
- warning校验
- 手动批量或精确校验字段能力
- 手动清除校验消息能力
- 更加校验规则扩展机制
- 增加正则规则扩展机制
- 增加校验消息模板引擎定制能力
- 更加完备的生命周期钩子
- 更加完备的路径解析引擎
- 解耦schema,更易扩展/二次开发(过去0.x的core是强耦合json schema的)
- @uform/react
- 包名变更,0.x @uform/react ---> 1.x @uform/react-schema-renderer,现在的@uform/react是一个全新的库,它只负责react组件的状态管理,无关json schema,若用户对UI定制需求高,则可直接使用该库进行开发
- 全新架构,基于React Hooks开发,更灵活,更易扩展
- 同样的actions/effects,但带来的是更大的想象空间
- @uform/react-schema-renderer
- 包含了过去0.x的@uform/react几乎100%的能力,只有少部分API有变化
- 升级表单协议,提供了更完备的json schema解析校验能力
- 更高效方便的Form/FormItem样式定制能力
- 提供了更加灵活的自定义组件开发方式,用来适配更复杂的业务场景
- @uform/antd or @uform/next
- 废除过去私有FormItem样式,全部继承组件库样式,所以不会再存在18px的空白占位符问题,同时也能支持对应组件库的主题样式定制能力
- 废除array组件的默认样式,统一采用cards样式,更加标准化,也能很好的适配组件库主题样式定制能力
- array table内部的table组件采用了对应组件库的table,可以适配更加复杂的table定制需求
- 支持了FormStep布局组件,可以非常方便的处理分步表单场景
- @uform/core
架构总览
整体来看,UForm未来是要打造一个完整的表单体系化的生态,目前UForm v1花了很大精力在研发核心底层,主要原因就是为了给上层生态体系打下坚实基础。
核心差异
@uform/core
已经完全重构,如果有用户直接基于v0 @uform/core开发的,是不能平滑迁移的,新版API变化非常大。
@uform/react
属于新库,如果有用户依赖@uform/react开发的,只需要将包名换为@uform/react-schema-renderer即可
@uform/react-schema-renderer
几乎100%迁移0.x的@uform/react能力,但还是存在少许break change。
如果除了这些还有和旧版不一致的地方,直接视为bug,走正常提issue流程即可,我们会尽快修复:
- 移除 registerFormFieldPropsTransformer API,如果要批量配置属性,可以在effects中批量配置
- 移除 registerFormWrapper API,使用registerFormItemComponent 替代
- 移除 calculateSchemaInitialValues API
- x-props含义改变,现在的x-props代表Field级别的扩展属性,比如FormItem的属性labelCol/wrapperCol等,现在组件级别的扩展属性使用x-component-props,不过这个改变在当前版本不会彻底改掉,所以还是向后兼容的,在未来的版本中很有可能移除兼容代码,或者将兼容代码抽离成第三方包。
@uform/antd or @uform/next
- Field组件改名SchemaMarkupField,目前向后兼容,可同时使用Field组件与SchemaMarkupField,后续考虑废弃此兼容项
- 移除帮助信息自动变成pop tips功能,目前title/dependencies都可以直接传react node
整体来看,API层面上基本无差别,更多的是样式层面上,因为是继承了原有组件库的Form/FormItem样式,在一些细微布局上会有差别
核心亮点
时间旅行能力
因为新版内核完全做了重构,采用了Observable Graph的数据结构来管理整个表单的状态,使得表单任何状态都是可观察的,表单状态也是Immutable的,所以我们可以轻易的实现时间旅行,这个能力主要在以下场景有较大价值:
- 开发者调试工具,可以开发一个devtools,方便开发者清晰的查看表单当前或过去的状态扭转情况
- 保存用户操作现场,当用户执行一系列操作之后,我们可以把表单整体状态保存起来,存储在localstorage中,在用户关机之后再打开相同页面,则可以完全复原之前的操作状态,注意:这个是操作状态,而不是单纯的表单数据,比如,用户在分步表单场景,用户操作停留在某一步,中途退出之后再打开该页面,只要使用时间旅行能力,则可以完完全全复原之前的操作状态,该在第几步,就在第几步,甚至你在某一步操作过程中出现了校验异常,在回滚过程中,也能把所有校验状态给恢复,如果用户还能将用户的浏览器滚动位置一并缓存,那基本上可以做到完美回滚了。
现在我们直接看代码:
import React from "react";
import ReactDOM from "react-dom";
import {
SchemaForm,
Field,
FormButtonGroup,
Submit,
createFormActions,
FormCard,
FormStep
} from "@uform/next";
import { Button } from "@alifd/next";
import "@alifd/next/dist/next.css";
const actions = createFormActions();
let cache = {};
const App = () => (
<SchemaForm actions={actions} labelCol={{ span: 8 }} wrapperCol={{ span: 6 }}>
<FormStep
style={{ marginBottom: 20 }}
dataSource={[
{ title: "基本信息", name: "step-1" },
{ title: "财务信息", name: "step-2" },
{ title: "条款信息", name: "step-3" }
]}
/>
<FormCard name="step-1" title="基本信息">
<Field name="a1" required title="A1" type="string" />
</FormCard>
<FormCard name="step-2" title="财务信息">
<Field name="a2" required title="A2" type="string" />
</FormCard>
<FormCard name="step-3" title="条款信息">
<Field name="a3" required title="A3" type="string" />
</FormCard>
<FormButtonGroup>
<Submit>提交</Submit>
<Button onClick={() => actions.dispatch(FormStep.ON_FORM_STEP_PREVIOUS)}>
上一步
</Button>
<Button onClick={() => actions.dispatch(FormStep.ON_FORM_STEP_NEXT)}>
下一步
</Button>
<Button
onClick={() => {
cache = actions.getFormGraph();
}}
>
存储当前状态
</Button>
<Button
onClick={() => {
actions.setFormGraph(cache);
}}
>
回滚状态
</Button>
</FormButtonGroup>
</SchemaForm>
);
ReactDOM.render(<App />, document.getElementById("root"));
想要直接看效果?点击:codesandbox.io/s/exciting-…
整个例子主要演示了分步表单,如何缓存表单状态,如果你停留在任何一个步骤中缓存了状态,再点击回滚就会立即回滚到对应的状态上。
完备的校验引擎
校验引擎属于除了表单状态管理之外的第二大核心能力,如果校验引擎做的不够好,不够完备,那么整体表单研发效率或者产品用户体验都会大打折扣。
validateFirst校验
validateFirst校验就是,如果给一个Field传入一组rules规则,那么当Field校验的时候,如果第一个校验不通过,则不会继续后续校验流程,这种校验模式比较适合前面的是同步校验,后面的是异步校验的场景
import React from "react";
import ReactDOM from "react-dom";
import { SchemaForm, Field, FormButtonGroup, Submit, Reset } from "@uform/next";
import Printer from "@uform/printer";
import "@alifd/next/dist/next.css";
const sleep = duration =>
new Promise(resolve => {
setTimeout(resolve, duration);
});
const App = () => (
<Printer>
<SchemaForm validateFirst labelCol={8} wrapperCol={6}>
<Field
name="aaa"
x-rules={[
{
required: true,
message: "不能为空"
},
{
async validator() {
await sleep(1000);
return "异步校验失败";
}
}
]}
type="string"
title="字段1"
/>
<FormButtonGroup sticky offset={8}>
<Submit>提交</Submit> <Reset>重置</Reset>
</FormButtonGroup>
</SchemaForm>
</Printer>
);
ReactDOM.render(<App />, document.getElementById("root"));
想直接看效果?请点击 codesandbox.io/s/smoosh-pi…
从上面的例子中我们可以看到,只要必填校验不通过,那么异步校验永远不会执行,如果我们关闭validateFirst,那么校验的时候就会全量校验,并合并错误消息。
warning校验
warning校验就是解决一些阈值设置类场景阈值设定到某个程度,会有提醒文案,但是不会阻止提交
import React from "react";
import ReactDOM from "react-dom";
import { SchemaForm, Field, FormButtonGroup, Submit, Reset } from "@uform/next";
import Printer from "@uform/printer";
import "@alifd/next/dist/next.css";
const App = () => (
<Printer>
<SchemaForm validateFirst labelCol={8} wrapperCol={6}>
<Field
name="aaa"
x-rules={value => {
if (value > 50) {
return {
type: "warning",
message: "阈值大于50可能会对业务造成负面影响"
};
}
}}
type="number"
title="阈值设置"
/>
<FormButtonGroup sticky offset={8}>
<Submit>提交</Submit> <Reset>重置</Reset>
</FormButtonGroup>
</SchemaForm>
</Printer>
);
ReactDOM.render(<App />, document.getElementById("root"));
想直接看效果?请点击 codesandbox.io/s/musing-we…
从上面的例子中,我们可以看到,在自定义校验器中,只需要返回一个type为warning的对象即可透出警告信息,但是并不会阻塞提交
失焦校验
失焦校验,主要是针对异步校验场景,可以大大减少重复请求次数
import React from "react";
import ReactDOM from "react-dom";
import { SchemaForm, Field, FormButtonGroup, Submit, Reset } from "@uform/next";
import Printer from "@uform/printer";
import "@alifd/next/dist/next.css";
const sleep = duration =>
new Promise(resolve => {
setTimeout(resolve, duration);
});
const App = () => (
<Printer>
<SchemaForm validateFirst labelCol={8} wrapperCol={6}>
<Field
name="aaa"
x-props={{
triggerType: "onBlur"
}}
x-rules={async value => {
if (Number(value) > 50) {
await sleep(2000);
return {
type: "warning",
message: "阈值大于50可能会对业务造成负面影响"
};
}
if (!/^\d+$/.test(value)) {
return "必须为数字";
}
}}
type="string"
title="阈值设置"
/>
<FormButtonGroup sticky offset={8}>
<Submit>提交</Submit> <Reset>重置</Reset>
</FormButtonGroup>
</SchemaForm>
</Printer>
);
ReactDOM.render(<App />, document.getElementById("root"));
想直接看效果?请点击 codesandbox.io/s/keen-ferm…
在上面例子中,我们可以在x-props上简单的配置triggerType即可快速处理失焦校验
手工批量校验/手工清除校验消息
某些场景,我们在一些联动交互过程中,可能需要人工去触发一批字段的校验逻辑,也可能需要人工去清除一批字段的校验逻辑
import React from "react";
import ReactDOM from "react-dom";
import {
SchemaForm,
Field,
FormButtonGroup,
createFormActions
} from "@uform/next";
import Printer from "@uform/printer";
import { Button } from "@alifd/next";
import "@alifd/next/dist/next.css";
const actions = createFormActions();
const App = () => (
<Printer>
<SchemaForm actions={actions} validateFirst labelCol={8} wrapperCol={6}>
<Field
name="aaa"
x-rules={[
{
required: true,
message: "不能为空"
},
{
pattern: /^\d+$/,
message: "必须为数字"
}
]}
type="string"
title="阈值设置"
/>
<FormButtonGroup sticky offset={8}>
<Button
onClick={() => {
actions.clearErrors("aaa"); //这里可以传FormPathPattern做批处理
}}
>
输入完成点我清空校验
</Button>
<Button
onClick={() => {
actions.validate("aaa"); //这里可以传FormPathPattern做批处理
}}
>
点我触发校验
</Button>
</FormButtonGroup>
</SchemaForm>
</Printer>
);
ReactDOM.render(<App />, document.getElementById("root"));
想直接看效果,请点击:codesandbox.io/s/wonderful…
校验规则扩展/正则规则扩展
考虑到业务私有规则沉淀,所以需要支持一种更加方便的校验规则扩展机制
import React from "react";
import ReactDOM from "react-dom";
import {
SchemaForm,
Field,
FormButtonGroup,
createFormActions,
registerValidationRules,
setValidationLocale,
registerValidationFormats
} from "@uform/next";
import Printer from "@uform/printer";
import { Button } from "@alifd/next";
import "@alifd/next/dist/next.css";
const actions = createFormActions();
setValidationLocale({
zh: {
custom: "必须以123开头"
}
});
registerValidationFormats({
custom: /^123.+/
});
registerValidationRules({
max12345: value => {
return Number(value) <= 12345 ? "必须大于12345" : "";
}
});
const App = () => (
<Printer>
<SchemaForm actions={actions} validateFirst labelCol={8} wrapperCol={6}>
<Field
name="aaa"
x-rules={[
{
format: "custom"
},
{
max12345: true
}
]}
type="string"
title="阈值设置"
/>
<FormButtonGroup sticky offset={8}>
<Button
onClick={() => {
actions.clearErrors("aaa"); //这里可以传FormPathPattern做批处理
}}
>
输入完成点我清空校验
</Button>
<Button
onClick={() => {
actions.validate("aaa"); //这里可以传FormPathPattern做批处理
}}
>
点我触发校验
</Button>
</FormButtonGroup>
</SchemaForm>
</Printer>
);
ReactDOM.render(<App />, document.getElementById("root"));
想直接看效果,请点击:codesandbox.io/s/hardcore-…
校验消息模板引擎
考虑到校验消息可能会存在富文本情况,所以需要支持模板引擎扩展能力,保证业务定制能力最大化
import React from "react";
import ReactDOM from "react-dom";
import {
SchemaForm,
Field,
FormButtonGroup,
createFormActions,
registerValidationRules,
setValidationLocale,
registerValidationFormats,
registerValidationMTEngine,
FormPath
} from "@uform/next";
import Printer from "@uform/printer";
import { Button } from "@alifd/next";
import "@alifd/next/dist/next.css";
const actions = createFormActions();
const template = (message, context) => {
const elements = [];
let lastOffset = 0;
message.replace(/\{\{\s*(\w+)\s*\}\}/g, (match, $0, offset) => {
elements.push(message.slice(lastOffset, offset));
elements.push(FormPath.getIn(context, $0));
lastOffset = offset + match.length;
return "";
});
if(elements.length <=0) return ;
return React.createElement("span", {}, ...elements);
};
registerValidationMTEngine(template);
setValidationLocale({
zh: {
custom: "必须以123开头,{{help}}"
}
});
registerValidationFormats({
custom: /^123.+/
});
registerValidationRules({
max12345: value => {
return Number(value) <= 12345 ? "必须大于12345,{{help}}" : "";
}
});
const App = () => (
<Printer>
<SchemaForm actions={actions} validateFirst labelCol={8} wrapperCol={6}>
<Field
name="aaa"
x-rules={[
{
format: "custom",
help: <a href="//github.com/alibaba/uform">查看帮助链接</a>
},
{
max12345: true,
help: <a href="//github.com/alibaba/uform">查看帮助链接</a>
}
]}
type="string"
title="阈值设置"
/>
<FormButtonGroup sticky offset={8}>
<Button
onClick={() => {
actions.clearErrors("aaa"); //这里可以传FormPathPattern做批处理
}}
>
输入完成点我清空校验
</Button>
<Button
onClick={() => {
actions.validate("aaa"); //这里可以传FormPathPattern做批处理
}}
>
点我触发校验
</Button>
</FormButtonGroup>
</SchemaForm>
</Printer>
);
ReactDOM.render(<App />, document.getElementById("root"));
想直接看效果,请点击 codesandbox.io/s/pensive-l…
整个案例给您呈现了一个超级强大的模板引擎扩展能力,轻松实现校验消息富文本化
标准的JSON Schema校验能力
之前有用户反馈,UForm的schema描述并不能支持原生JSON Schema的校验属性,比如pattern/max这些标准校验属性,都是通过x-rules来配置的,这次,我们将所有的JSON Schema相关的校验属性统一支持了一遍,当然,其实内部实现,都是转换成x-rules,只是用户感知不到
import React from "react";
import ReactDOM from "react-dom";
import { SchemaForm, Field, createFormActions } from "@uform/next";
import Printer from "@uform/printer";
import "@alifd/next/dist/next.css";
const actions = createFormActions();
const App = () => (
<Printer>
<SchemaForm actions={actions} validateFirst labelCol={8} wrapperCol={6}>
<Field name="aaa" required maximum={100} type="string" title="阈值设置" />
<Field name="bbb" format="url" type="string" title="URL" />
</SchemaForm>
</Printer>
);
ReactDOM.render(<App />, document.getElementById("root"));
想直接看效果,请点击 codesandbox.io/s/withered-…
Vue适配能力
因为内核的完全重构,从设计上也更加贴合vue的设计理念,所以,UForm便可以轻松的适配vue技术栈,这里是基于@uform/core快速实现了@uform/vue原型代码,感兴趣的同学可以移步:codesandbox.io/s/vue-templ…
状态管理能力/局部副作用管理能力
UForm内部提供了一个完备的响应式状态管理系统,我们可以使用它做一些额外的状态管理,同时,这些状态最终都会挂到FormGraph上,所以也会享受到FormGraph时间旅行能力,还是针对上面分步表单的例子,这次我们直接看源码:
//注册控制器组件
const FormStep = createControllerBox<IFormStep>(
'step',
({ form, schema, children }) => {
//通过useFieldState设置当前字段默认状态,同时获取操作函数
const [{ current }, setFieldState] = useFieldState({
current: 0
})
const ref = useRef(current)
const { dataSource, ...stepProps } = schema.getExtendsComponentProps()
const items = toArr(dataSource)
const update = (cur: number) => {
form.notify(StateMap.ON_FORM_STEP_CURRENT_CHANGE, {
value: cur,
preValue: current
})
//更新当前字段状态,该状态最终会挂到FormGraph上的fieldState上去
setFieldState({
current: cur
})
}
//局部副作用管理,用于实现复杂自定义组件的逻辑功能
useFormEffects(($, { setFieldState }) => {
items.forEach(({ name }, index) => {
setFieldState(name, (state: any) => {
state.display = index === current
})
})
$(StateMap.ON_FORM_STEP_CURRENT_CHANGE).subscribe(({ value }) => {
items.forEach(({ name }, index) => {
if (!name)
throw new Error('FormStep dataSource must include `name` property')
setFieldState(name, (state: any) => {
state.display = index === value
})
})
})
$(StateMap.ON_FORM_STEP_NEXT).subscribe(() => {
form.validate().then(({ errors }) => {
if (errors.length === 0) {
update(
ref.current + 1 > items.length - 1 ? ref.current : ref.current + 1
)
}
})
})
$(StateMap.ON_FORM_STEP_PREVIOUS).subscribe(() => {
update(ref.current - 1 < 0 ? ref.current : ref.current - 1)
})
$(StateMap.ON_FORM_STEP_GO_TO).subscribe(payload => {
if (!(payload < 0 || payload > items.length)) {
update(payload)
}
})
})
ref.current = current
return (
<Fragment>
<Steps {...stepProps} current={current}>
{items.map((props, key) => {
return <Steps.Step {...props} key={key} />
})}
</Steps>{' '}
{children}
</Fragment>
)
}
)
具体源码地址:packages/antd/src/components/FormStep.tsx
整体看下来,想要实现一个带逻辑的布局组件,我们只需要使用createControllerBox即可,同时想要在布局组件或者自定义组件内实现逻辑功能,我们就使用useFieldState和useFormEffects即可,当然,我们还提供了useFormState的能力,不过在自定义组件或者布局组件中不太推荐使用它,因为它会监听FormState变化重新渲染,这样对性能会有影响,所以我们更推荐用户直接从props上取form实例,从实例中直接拿状态即可。
Effects复用能力(Effect Hook)
考虑到用户在实现业务逻辑的过程中,其实也会存在大量可复用的业务逻辑,即便是在副作用这一层,所以,我们支持了Effects可复用能力
import React from "react";
import ReactDOM from "react-dom";
import {
SchemaForm,
Field,
createFormActions,
createEffectHook
} from "@uform/next";
import { Button } from "@alifd/next";
import Printer from "@uform/printer";
import "@alifd/next/dist/next.css";
const dyiHook$ = createEffectHook("onDiyChange");
const outerActions = createFormActions();
const getCustomEffects = () => {
const innerActions = createFormActions();
dyiHook$().subscribe(() => {
innerActions.setFieldState("aaa", state => {
state.value = "123";
});
});
};
const App = () => (
<Printer>
<SchemaForm
actions={outerActions}
effects={() => {
getCustomEffects();
}}
validateFirst
labelCol={8}
wrapperCol={6}
>
<Field name="aaa" required type="string" title="AAA" />
<Field name="bbb" required type="string" title="BBB" />
<Button
onClick={() => {
outerActions.dispatch("onDiyChange");
}}
>
触发自定义事件
</Button>
</SchemaForm>
</Printer>
);
ReactDOM.render(<App />, document.getElementById("root"));
想直接看效果,请点击 codesandbox.io/s/cranky-ch…
以上例子演示了我们可以自定义生命周期钩子函数,同时这个钩子函数可以在任何函数体内直接使用,同时,createFormActions API目前也支持了可以再任何函数体内使用的能力,其实它内部会自动获取outerActions并透传给每个自定义effects函数
超复杂自定义组件
什么才是超复杂自定义组件:
- 组件内部存在大量表单组件,同时内部也存在大量联动关系
- 组件内部存在私有的服务端动态渲染方案
- 组件内部有复杂布局结构
对于超复杂自定义组件,在旧版中很难实现,主要受限于表单数据和校验不能更好的同步。
import React from "react";
import ReactDOM from "react-dom";
import {
SchemaForm,
Field,
InternalField,
registerFormField,
useFormEffects,
FormEffectHooks,
FormPath,
FormButtonGroup,
Submit
} from "@uform/next";
import { Form, Input } from "@alifd/next";
import Printer from "@uform/printer";
import "@alifd/next/dist/next.css";
const FormItem = ({ component, ...props }) => {
return (
<InternalField {...props}>
{({ state, mutators }) => {
const messages = [].concat(state.errors || [], state.warnings || []);
let status = "";
if (state.loading) {
status = "validating";
}
if (state.invalid) {
status = "error";
}
if (state.warnings && state.warnings.length) {
status = "warning";
}
return (
<Form.Item
{...props}
help={messages.length ? messages : props.help && props.help}
validateStatus={status}
>
{React.createElement(component, {
...state.props,
value: state.value,
onChange: mutators.change,
onBlur: mutators.blur,
onFocus: mutators.focus
})}
</Form.Item>
);
}}
</InternalField>
);
};
//不用connect包装
registerFormField("complex", ({ path }) => {
useFormEffects(({ setFieldState }) => {
FormEffectHooks.onFieldValueChange$("ccc").subscribe(({ value }) => {
if (value === "123") {
setFieldState("ddd", state => {
state.value = "this is linkage relationship";
});
}
});
});
return (
<div>
<FormItem
label="AA"
name={FormPath.parse(path).concat("aaa")}
component={Input}
/>
<FormItem
label="BB"
name={FormPath.parse(path).concat("bbb")}
component={Input}
/>
<FormItem label="CC" name="ccc" component={Input} />
<FormItem label="DD" name="ddd" component={Input} />
</div>
);
});
const App = () => (
<Printer>
<SchemaForm validateFirst labelCol={8} wrapperCol={6}>
<Field type="complex" name="aaa" />
<FormButtonGroup>
<Submit>提交</Submit>
</FormButtonGroup>
</SchemaForm>
</Printer>
);
ReactDOM.render(<App />, document.getElementById("root"));
想要直接看效果?请点击 codesandbox.io/s/kind-reso…
在这个例子中,我们主要使用了两个核心 API,主要是 useFormEffects 和 InternalField,useFormEffects 给开发者提供了局部写 effects 逻辑的地方这样就能很方便的复用 effects 逻辑,InternalField 则就是@uform/react 的 Field 组件,这个可以具体看看@uform/react 的文档,因为 SchemaForm 内部也是使用的@uform/react,所以可以共享同一个 Context,所以我们就能很方便的在自定义组件内使用 InternalField,同时需要注意一点,直接使用 InternalField 的时候,我们注册的 name 是根级别的 name,如果想要复用当前自定义组件的路径,可以借助 FormPath 解析路径,然后再 concat 即可。
升级指南
如果您现在一直在使用@uform/antd或者@uform/next,同时,您也没有使用前面所说的@uform/react-schema-renderer被废弃的API,那么完全可以无缝平滑升级,当然,使用过程中肯定会遇到bug,这个只需要您积极的在github上提issue即可,我们会尽快修复。
品牌升级
因为UForm已经是一个集团共建的技术产品了,而不是个人或单个团队的技术产品,所以需要在品牌命名上考虑整体的品牌文化建设,经过内部多次讨论,最终我们确定了一个全新的技术品牌,大概在今年年底的时候,UForm会以全新的品牌姿势给大家见面,敬请期待。
Q/A
-
UForm v1预计什么时候正式发布?
答:目前已经发布alpha版本,同时我们也正在内部业务验证中,等业务验证差不多,基本稳定之后便可以正式发布,大概率在12月中旬即可正式发布。
-
UForm Vue适配版预计什么时候投入开发?
答:因为目前Vue也在着手3.0的开发,所以我们打算后续主要基于3.0版本的API来开发,因为3.0对Typescript更加友好,也更加FP,UForm希望保持技术新鲜性,所以未来会直接基于3.0开发。
-
UForm品牌升级之后的版本会与UForm v1出现API大变化吗?
前面提到的被废弃的API,同时已经内置已兼容的,后续很有可能不会内置兼容逻辑,我们可能会考虑提供polyfill单独包来提供兼容逻辑
-
UForm 小程序适配版预计什么时候投入开发?
答:目前还在调研设计阶段,后续是有这方面规划的,可能主要会从集团标准小程序着手投入,同时也会从rax方向着手投入
-
UForm的Form Schema什么时候能描述逻辑?
答:目前还在设计阶段,这部分的东西属于一个独立且厚重的模块,需要详细设计,并验证。
-
UForm开发者工具预计什么时候发布?
答:预计明年年初开始投入开发。