前言
通过模仿 rc-form 的实现思路,可以学习一下 hoc 的使用场景。
暴露 createForm() 函数
通过 createForm 函数,返回一个组件。该组件拓展了我们的一些方法。
export function createForm(Cmp) {return class extends Component {getFieldDecorator = () => {}getFieldsValue = () => {}getFieldValue = () => {}setFieldValue = () => {}validateFields = () => {}form = () => {return {getFieldDecorator: this.getFieldDecorator,getFieldValue: this.getFieldValue,getFieldsValue: this.getFieldsValue,setFieldValue: this.setFieldValue,validateFields: this.validateFields,}}render() {const form = getForm()return <Cmp {...this.props} form={form} />}}}
实现 getFieldDecorator
使用方法
<div>{getFieldDecorator('username', {rules: { require: true, message: '请输入用户名' })(<input placeholder="请输入用户名" />)}</div>
可以看出,该函数接收两个参数,第一个是字段名,第二个是它的规则。又接着返回了一个接收一个组件的函数,最终返回一个加工后的组件。现在我们开始简单实现一下。
constructor(){// ...this.state = {}this.options = {}}
getFieldDecorator = (fieldName, option) => InputCmp => {// 保存数据和选项if (this.state[fieldName] === undefined) this.setState({ [fieldName]: '' })this.options[fieldName] = option// 返回一个处理后的组件return React.cloneElement(InputCmp, {name: fieldName,value: this.state[fieldName],onChange: this.handleChange,})}
定义 handleChange 事件
handleChange = (e) => {const { name, value } = e.targetthis.setState({ [name]: value })}
实现 getFieldValue
直接把 state 的数据返回即可
getFieldsValue = () => {return {...this.state}}
实现 getFieldsValue
通过传进来的名字,返回对应的数据
getFieldValue = name => {return this.state[name]}
实现 setFieldValue
直接将传进来的数据合并起来即可
setFieldValue = state => {this.setState(state)}
实现 validateFields
该方法接收一个回调函数,通过遍历options的规则,在判断相应的值,返回错误数据以及state的数据
validateFields = callback => {const err = []for (let fieldName in this.options) {const rules = this.options[fieldName]?.rulesconst value = this.state[fieldName]if(rules && rules.require && rules.message && !value) {err.push({[fieldName]: rules.message})}}// 判断 err 是否有数据if (err.length === 0) {callback(null, { ...this.state })} else {callback(err, { ...this.state })}}
最终代码
import React, { Component } from 'react'export function createForm(Cmp) {return class extends Component {constructor(props) {super(props)this.state = {}this.options = {}}handleChange = e => {const { name, value } = e.targetthis.setState({ [name]: value })}validateFields = callback => {const err = []for (let fieldName in this.options) {const rules = this.options[fieldName]?.rulesif (rules && rules.require && rules.message && !this.state[fieldName]) {err.push({[fieldName]: rules.message,})}}if (err.length === 0) {callback(null, { ...this.state })} else {callback(err, { ...this.state })}}getFieldDecorator = (fieldName, option) => InputCmp => {this.options[fieldName] = optionif (this.state[fieldName] === undefined) this.setState({ [fieldName]: '' })return React.cloneElement(InputCmp, {name: fieldName,value: this.state[fieldName],onChange: this.handleChange,})}getFieldValue = name => {return this.state[name]}getFieldsValue = () => {return { ...this.state }}setFieldValue = state => {this.setState(state)}getForm = () => {return {getFieldDecorator: this.getFieldDecorator,getFieldValue: this.getFieldValue,getFieldsValue: this.getFieldsValue,setFieldValue: this.setFieldValue,validateFields: this.validateFields,}}render() {const form = this.getForm()return <Cmp {...this.props} form={form} />}}}
