前言

通过模仿 rc-form 的实现思路,可以学习一下 hoc 的使用场景。

暴露 createForm() 函数

通过 createForm 函数,返回一个组件。该组件拓展了我们的一些方法。

  1. export function createForm(Cmp) {
  2. return class extends Component {
  3. getFieldDecorator = () => {}
  4. getFieldsValue = () => {}
  5. getFieldValue = () => {}
  6. setFieldValue = () => {}
  7. validateFields = () => {}
  8. form = () => {
  9. return {
  10. getFieldDecorator: this.getFieldDecorator,
  11. getFieldValue: this.getFieldValue,
  12. getFieldsValue: this.getFieldsValue,
  13. setFieldValue: this.setFieldValue,
  14. validateFields: this.validateFields,
  15. }
  16. }
  17. render() {
  18. const form = getForm()
  19. return <Cmp {...this.props} form={form} />
  20. }
  21. }
  22. }

实现 getFieldDecorator

使用方法

  1. <div>
  2. {getFieldDecorator('username', {rules: { require: true, message: '请输入用户名' })(<input placeholder="请输入用户名" />)}
  3. </div>

可以看出,该函数接收两个参数,第一个是字段名,第二个是它的规则。又接着返回了一个接收一个组件的函数,最终返回一个加工后的组件。现在我们开始简单实现一下。

  1. constructor(){
  2. // ...
  3. this.state = {}
  4. this.options = {}
  5. }
  1. getFieldDecorator = (fieldName, option) => InputCmp => {
  2. // 保存数据和选项
  3. if (this.state[fieldName] === undefined) this.setState({ [fieldName]: '' })
  4. this.options[fieldName] = option
  5. // 返回一个处理后的组件
  6. return React.cloneElement(InputCmp, {
  7. name: fieldName,
  8. value: this.state[fieldName],
  9. onChange: this.handleChange,
  10. })
  11. }

定义 handleChange 事件

  1. handleChange = (e) => {
  2. const { name, value } = e.target
  3. this.setState({ [name]: value })
  4. }

实现 getFieldValue

直接把 state 的数据返回即可

  1. getFieldsValue = () => {
  2. return {...this.state}
  3. }

实现 getFieldsValue

通过传进来的名字,返回对应的数据

  1. getFieldValue = name => {
  2. return this.state[name]
  3. }

实现 setFieldValue

直接将传进来的数据合并起来即可

  1. setFieldValue = state => {
  2. this.setState(state)
  3. }

实现 validateFields

该方法接收一个回调函数,通过遍历options的规则,在判断相应的值,返回错误数据以及state的数据

  1. validateFields = callback => {
  2. const err = []
  3. for (let fieldName in this.options) {
  4. const rules = this.options[fieldName]?.rules
  5. const value = this.state[fieldName]
  6. if(rules && rules.require && rules.message && !value) {
  7. err.push({
  8. [fieldName]: rules.message
  9. })
  10. }
  11. }
  12. // 判断 err 是否有数据
  13. if (err.length === 0) {
  14. callback(null, { ...this.state })
  15. } else {
  16. callback(err, { ...this.state })
  17. }
  18. }

最终代码

  1. import React, { Component } from 'react'
  2. export function createForm(Cmp) {
  3. return class extends Component {
  4. constructor(props) {
  5. super(props)
  6. this.state = {}
  7. this.options = {}
  8. }
  9. handleChange = e => {
  10. const { name, value } = e.target
  11. this.setState({ [name]: value })
  12. }
  13. validateFields = callback => {
  14. const err = []
  15. for (let fieldName in this.options) {
  16. const rules = this.options[fieldName]?.rules
  17. if (rules && rules.require && rules.message && !this.state[fieldName]) {
  18. err.push({
  19. [fieldName]: rules.message,
  20. })
  21. }
  22. }
  23. if (err.length === 0) {
  24. callback(null, { ...this.state })
  25. } else {
  26. callback(err, { ...this.state })
  27. }
  28. }
  29. getFieldDecorator = (fieldName, option) => InputCmp => {
  30. this.options[fieldName] = option
  31. if (this.state[fieldName] === undefined) this.setState({ [fieldName]: '' })
  32. return React.cloneElement(InputCmp, {
  33. name: fieldName,
  34. value: this.state[fieldName],
  35. onChange: this.handleChange,
  36. })
  37. }
  38. getFieldValue = name => {
  39. return this.state[name]
  40. }
  41. getFieldsValue = () => {
  42. return { ...this.state }
  43. }
  44. setFieldValue = state => {
  45. this.setState(state)
  46. }
  47. getForm = () => {
  48. return {
  49. getFieldDecorator: this.getFieldDecorator,
  50. getFieldValue: this.getFieldValue,
  51. getFieldsValue: this.getFieldsValue,
  52. setFieldValue: this.setFieldValue,
  53. validateFields: this.validateFields,
  54. }
  55. }
  56. render() {
  57. const form = this.getForm()
  58. return <Cmp {...this.props} form={form} />
  59. }
  60. }
  61. }