导语 身为一个前端开发,相信诸如“如果这一项选择了A,那一项就只能选B”的需求,大家一定遇到过。“表单联动”是前端开发中的经典场景。本文对于表单联动问题的解决方案进行一个简单的探讨。
表单联动是前端经常面临的问题,联动实际上是一组表单项和表单项之间的依赖关系的集合。比如经典的“省-市-区”三级联动,就包含了“省”、“市”、“区”三个表单项,以及“市->省”和“区->市”。表单项的依赖关系可以抽象成若干if(省 === '广东省') { 市 in [‘广州市’, ‘深圳市’, ...] }
的形式,即“市”依赖于“省”。(被依赖项也可以有多个,比如C依赖于B和A;而依赖项有多个的情况可以拆解为相互独立的依赖关系)
基于这样的关系,表单项之间的依赖关系其实就可以用有向图来解释。表单项是图中的各个节点,而依赖关系就是图中的有向弧。以经典的“省-市-区”关系来举例,建立如下的图模型:
可以看到,“省-市-区”的依赖关系是一个相对简单的模式,可以用“依赖链”来表示。根据依赖关系的复杂程度,“依赖图”可以简化成“依赖树”或者“依赖链”。
对于简单的依赖关系,只需要简单地监听被依赖项的变化,再更新依赖项的范围即可。比如(React DEMO):
<Select value={province} options={PROVINCE_OPTIONS} onChange={(v) => setProvice(v)} />
<Select value={city} options={getCityOptions(province)} onChange={(v) => setCity(v)} />
<Select value={district} options={getDistrictOptions(city)} onChange={(v) => setDistrict(v)} />
而当依赖关系变得更加复杂时,这种分布式管理的方式,会让依赖关系变得难以维护。比如下方的略显复杂依赖模型:
这一类比较复杂的依赖关系,没有办法简化成“链”或者“树”的形式。如果依然使用监听被依赖项的变化来更新表单,会产生重复更新的问题。如上图中的依赖关系,当“地域”更新之后,会同时触发“套餐包内容”和“可用区的变化”,最终导致“购买时长”触发了两次更新。
基于表单依赖关系是一个有向图,如果能够保证这个图中是一个有向无环图,我们就可以使用拓扑排序来生成一个表单项的更新序列。
简单介绍拓扑排序的思路,就是每次从当前图中找出入度为0(没有箭头指向)的点,压入队列,并从图中删除该节点,最终生成一个排序队列。以上文中的例子来看,我们就可以得到一个更新队列:['套餐包类型', '地域', '套餐包内容', '可用区', '购买时长']
。(1、队列顺序并不唯一;2、因为笔者表示依赖关系的有向弧指向和典型的拓扑排序的要求相反,这里实际实现时是取了出度为0的点)
在有了这样一个更新队列之后,可以集中管理表单的依赖关系。在监听到表单项的更新之后,拷贝一个表单的草稿,按照更新队列的顺序更新草稿,再整体更新表单。比如(React DEMO):
import produce from 'immer';
// ...
const onChange = (key, newValue) => {
const newValues = produce(values, draft => {
// 更新草稿draft
});
setFormValues(newValues)
}
// ...
<Form value={values} onChange={onChange}>
{ // ... }
</Form>
拓扑排序的成立条件之一是当前的图中不存在环。在依赖图中,如果出现了环形结构,就意味着依赖关系中存在循环依赖。大部分情况下,循环依赖的关系在需求阶段就可以发现并避免;但仍然有某些情况下,循环依赖会不那么容易被发现。比如下面的一段描述:
在选择“省份”后,“高校”一栏只能选择该省份的学校;在选择“高校”之后,“省份”一栏自动选取当前学校的所在省份。
在这里“省份”和“高校”之间就产生了一个循环依赖。产生这种问题的原因,就是表单在初始情况下的依赖关系不明确,各个表单项处在一个全部可选的状态。规避这种问题,就需要将表单项的依赖关系收敛到一个不存在环的状态,笔者想到的解决方案有两个:
前端对于表单的解决方案已经有很多很好的实践。比如react-final-form
等优秀的开源框架,能够帮助我们解决大部分表单需求。只是在遇到表单需求时,有时候并不能简单地一把梭,而是需要我们对问题进行一些简单的分析。首先确保当前需求明确、设计合理,之后再着手coding,才能达到事半功倍的效果。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。