我们先来看一段代码,看看我们要实现的组件是怎么使用的:
1 | import React, {Component, useEffect} from 'react' |
框架搭建
我们先把我们组件的架子搭起来,新建 my-rc-field-form
目录:
1 | my-rc-field-form |
其中,index.js
中内容如下:
1 | import _Form from './Form' |
我们先简单的实现下 Field.js
:
1 | export default class Field extends Component { |
然后是 Form.js
:
1 | export default function Form({form, children, onFinish, onFinishFailed}) { |
再然后是 useForm.js
:
1 | export default function useForm() { |
表单数据仓库
分析我们的需求,我们发现 useForm
返回的对象上面有 getFieldValue
, setFieldsValue
等方法可以来操作我们的表单数据。看来我们需要有个地方存储我们所有的表单数据,我们叫它 FormStore
:
1 | class FormStore { |
执行 useForm
的时候,需要实例化一个 FormStore
:
1 | export default function useForm(form) { |
这里有个问题,数据更新发生在 Field.js
中,如何能够让其更新 FormStore
中的数据呢,这里我们使用 React.createContext
:
1 | export default function Form({form, children, onFinish, onFinishFailed}) { |
现在就可以在 Field.js
中对数据进行获取和更新了:
1 | export default class Field extends Component { |
不过,现在虽然数据得到了更新,但是组件却无法更新。注意到我们这里写了一个函数 onStoreChange
,里面调用了 forceUpdate
。看样子我们只需要在每次修改 FormStore
的数据时,去调用这个函数就可以了。
1 | setFieldsValue = (newStore) => { |
但是,这里又有一个问题了。我们怎么拿到 Field
的引用呢?我们在每个 Field
挂载的时候去 FormStore
中进行注册一下就行了:
1 | class FormStore { |
1 | export default class Field extends Component { |
现在,我们可以在 setFieldsValue
中调用 Filed
的 onStoreChange
了:
1 | setFieldsValue = (newStore) => { |
这样,我们就实现了数据更新的时候去更新组件。
表单验证
有了之前的基础,表单验证也好做了,我们增加 submit
和 validate
方法:
1 | validate = () => { |
然后修改一下 Form
组件:
1 | export default function Form({form, children, onFinish, onFinishFailed}) { |
在类组件中使用
注意到 antd4 中的 Form
是可以在类组件中使用的:
1 | export default class extends React.Component { |
但是,我们自己实现的 Form
组件是函数式组件,怎么获取到 ref
呢?这就需要 React.forwardRef
和 React.useImperativeHandle
来帮忙了,我们改造下 Form
组件:
1 | export default React.forwardRef( |
总结
从这个例子当中,我们学到以下几点:
- 数据集中管理的思想
- Context 的使用
- 组件注册的思想
React.forwardRef
和React.useImperativeHandle
的用法