我们先来看一段代码,看看我们要实现的组件是怎么使用的:
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的用法