1、原方案:
// 命中依赖,一次只能命中一层
const onValuesChange = (changeValue: any, formDataSource: any) => {
// 依赖处理: 组件的props,value
// ...
if (show || propsChange){
// ...
Object.assign(createFormItems[key], updateItemProps);
}
if (valueChange) {
// ...
// 不会触发主组件更新
form.setFieldValue(key, newFormValues);
}
// ...
// 保存 子级被依赖字段,同时触发本次依赖命中的更新 触发渲染
if (newRunningReliedKey && !!newRunningReliedKey.length) {
setRunningReliedKey(newRunningReliedKey);
} else {
// 最后一层依赖的更新 触发渲染
setRerender(Date.now());
}
}
2、缺陷:
2.1 原逻辑:
简单,非常简单
1、收集 表单项的所有依赖关系dependencies;
2、由Form组件的onValuesChange,来触发依赖项的动态更新,并在onValuesChange中收集子级依赖更新项,再手动(setState)触发下一轮(useEffect)的onValuesChange;
3、依赖项的数据源,只有当前表单的所有值(form.getFieldsValue)
2.2 缺陷:
1、触发依赖执行
form.setFieldsValue :
由form.setFieldsValue触发的valuesChange,无法被onValuesChange捕捉到,也不会触发主组件的reRenderFields
而最重要的是,form.setFieldsValue 的使用时机,是不受约束的,很难控制和定向捕捉的,所以一旦在外部组件触发了,而开发者未知的时候,是很难排查到的
尽管做了form.setFieldsValue之后同时执行setRerenderFields(setState会触发组件更新),来弥补:
// 依赖处理 定义
propsChange(){
// ...
form.setFieldsValue({ ...});
setRerenderFields({ ...})
// ...
}
// 依赖处理 执行
useEffect(() => {
onValuesChange({ ...reRenderFields})
}), [reRenderFields])
但是仍旧很难控制,而且setRerender的执行散落在依赖控制的外部,就很难理解,也很难收集和控制,这对使用者的心智成本要求太高了,包括我自己,真是小心又小心,累惨,对后期维护和升级的成本也大大提升,万万不能写这种埋雷的代码
2、外部数据源
还有一个很难处理的问题,就是外部数据源,有时候,表单项的动态变化,依赖的不止是当前表单的数据,还会依赖外部状态,比如说,接口请求数据、路由数据、环境(上级组件等)数据等,都可能触发更新;
表单的渲染状态,renderStatus,是edit?create?view?会覆盖掉表单项的禁用状态;
表单initialValues,initialValues会被表单项之间的动态依赖给覆盖掉,导致初始化数据无法正常展示;
全局数据源,比如说,routeInfo等,怎么传进去呢?而且传太多的外部参数,会破坏依赖处理功能的单一性原则,实在棘手;
表单项的数据源,比如说Select这种组件,其数据源,有时候是接口请求来的,尽管我用了withQueryHOC(Select)来收敛,让表单项各自维护各自的远程数据源,但是,防不住,A组件的远程数据源,会被B组件依赖呀,这样被收敛之后么,就无法在外部共享这些内部数据源了
3、缺陷处理:
3.1 依赖触发
useWatch,可以捕捉所有的表单项的值的更新
既然form.setFieldsValue不能被捕捉,那么直接放弃onValuesChange作为触发依赖执行的入口,使用useWatch来处理,缺点是平铺很多useWatch和useEffect,无法做依赖触发的统一收集,但是基本符合react组件的更新逻辑,没有额外的更新拦截,是安全的,而且这种依赖平铺,也是比较清晰明了,代码可读性比较好;
下一步,将依赖关系单独配置出来,在useEffect中单独处理相应的dependencies handlers,这样可以解耦render content的内容,无论是配置化生成的renderComponentList,还是标签组件格式平铺的,都可以兼容
3.2 表单项的数据源:
从原收敛的withQueryHoc,解开,由外部useHook来统一生产所有表单项的远程数据源,或者也可以一并兼容固定数据源
原收敛方案:
后续优化useQuerysHook:
同时覆写原配置项的外部数据源
4、总结
核心就是:
数据驱动(configSchema)
业务逻辑分层(数据源、数据或业务处理、UI)(useHook)
组件职责收敛(withHOC),组件自治