如何用RxJS实现Redux Form(2)

举个例子,假设在一个 Form 中有三个 Field (如下),当只有 FieldA 的 value 发生变化时, 为了不让 <Form/> 和
其子组件也 re-render,Redux Form 内部需要通过 shouldComponentUpdate() 去限制。

// 伪代码 <Form> <FieldA/> <FieldB/> <FieldC/> </Form>

而 RxJS 能把组件更新的粒度控制到最小,换句话说,就是让真正需要 re-render 的 <Field/> re-render,而不需要 re-render 的组件不重新渲染 。

核心是 Subject

从上面的设计思路可以总结出以下两个问题:

Form 和 Field 是一对多的关系,form 的状态需要通知给多个 Field。

Field 需要根据数据去修改组件的状态。

第一个问题,需要的是一个 Observable 的功能,而且是能够支持多播的 Observable。第二个问题需要的是一个 Observer 的功能。在 RxJS 中,既是 Observable 又是 Observer,而且还能实现多播的,不就是 Subject 么!因此,在实现 Form 时,会大量用到 Subject。

formState 数据结构

Form 组件中也需要一个 State,用来保存所有 Field 的状态,这个 State 就是 formState。

那么 formState 的结构应该如何定义呢?

在最早的版本中,formState 的结构是长下面这个样子的:

interface IFormState { [fieldName: string]: { dirty?: boolean; touched?: boolean; visited?: boolean; error?: TError; value: string; }; }

formState 是一个对象,它以 fieldName 为 key,以一个 保存了 Field 状态的对象作为它的 value。

看起来没毛病对吧?

但是。。。。。

最后 formState 的结构却变成了下面这样:

interface IFormState { fields: { [fieldName: string]: { dirty?: boolean; touched?: boolean; visited?: boolean; error?: string | undefined; }; }; values: { [fieldName: string]: any; }; }

Note: fields 中不包含 filed value,只有 field 的一些状态信息。values 中只有 field values。

为什么呢???

其实在实现最基本的 Form 和 Field 组件时,以上两种数据结构都可行。

那问题到底出在哪儿?

这里先买个关子,目前你只需要知道 formState 的数据结构长什么样就可以了。

数据流

如何用RxJS实现Redux Form

为了更好的理解数据流,让我们来看一个简单的例子。我们有一个 Form 组件,它的内部包含了一个 Field 组件,在 Field 组件内部又包含了一个 Text Input。数据流可能是像下面这样的:

用户在输入框中输入一个字符。

Input 的 onChange 事件会被 Trigger。

Field 的 onChange Action 会被 Dispatch。

根据 Field 的 onChange Action 对 formState 进行修改。

Form State 更新之后会通知 Field 的观察者。

Field 的观察者将当前 Field 的 State pick 出来,如果发现有更新则 setState ,如果没有更新则什么都不做。

setState 会使 Field rerender ,新的 Field Value 就可以通知给 Input 了。

核心组件

首先,我们需要创建两个基本组件,一个 Field 组件,一个 Form 组件。

Field 组件

Field 组件是连接 Form 组件和表单元素的中间层。它的作用是让 Input 组件的职责更单一。有了它之后,Input 只需要做显示就可以了,不需要再关心其他复杂逻辑(validate/normalize等)。况且,对于 Input 组件来说,不仅可以用在 Form 组件中,也可以用在 Form 组件之外的地方(有些地方可能并不需要 validate 等逻辑),所以 Field 这一层的抽象还是非常重要的。

拦截和转换。 format/parse/normalize。

表单校验。 参考 HTML Form 的表单校验,我们可以把 validation 放在 Field 组件上,通过组合验证规则来适应不同的需求。

触发 field 状态的 改变(如 touched,visited)

给子组件提供所需信息。 向下提供 Field 的状态 (error, touched, visited...),以及用于表单元素绑定事件的回调函数 (onChange,onBlur...)。

利用 RxJS 的特性来控制 Field 组件的更新,减少不必要的 rerender。

与 Form 进行通信。 当 Field 状态发生变化时,需要通知 Form。在 Form 中改变了某个 Field 的状态,也需要通知给 Field。

Form 组件

管理表单状态。 Form 组件将表单状态提供给 Field,当 Field 发生变化时通知 Form。

提供 formValues。

在表单校验失败的时候,阻止表单的提交。

通知 Field 每一次 Form State 的变化。 在 Form 中会创建一个 formSubject&dollar;,每一次 Form State 的变化都会向 formSubject&dollar; 上发送一个数据,每一个 Field 都会注册成为 formSubject&dollar; 的观察者。也就是说 Field 知道 Form State 的每一次变化,因此可以决定在适当的时候进行更新。
当 FormAction 发生变化时,通知给 Field。 比如 startSubmit 的时候。

组件之间的通信

1、Form 和 Field 通信。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/6d9f3e7fe4431ed38f048de92b3a28a4.html