zl程序教程

您现在的位置是:首页 >  其它

当前栏目

antd4与antd3Form表单设计区别

区别 设计 表单
2023-06-13 09:13:56 时间

核心

antd3思想:使用HOC(高阶组件)包裹form表单,HOC组件中的state存储所有的value值,定义设置值和获取值的方法 缺点:动一发牵全身,一个value值改变,因为这是顶级状态,所以所有的子组件都会因父组件的重新render而render,浪费了性能

antd4思想:使用Context 包裹form表单,自定义一个store类,存储所有的表单value值,定义设置值和获取值得方法,因为不是组件内部状态,需要自己定义更新函数,在每个Form.Item中定义forceUpdate()强制更新函数,当我们setValue值得时候,根据name值判断出要更新的Form.Item,可以调用该Item的更新函数更新,相比ant3节约了性能(个人觉得这个思想类似Vue了,涉嫌抄袭) ant4简单原理展示 基本用法,拷贝整理下可测试

//FormPage页面 函数组件和类组件略有差异,类组件会用到神奇的React.forwardRef api 和 useImperativeHandle  hook8l
import React, { useEffect, Component } from 'react'
import Input from './../components/Input'
import Form, { Field, useForm } from './../components/Form'
// export default function FormPage() {
//     const [form] = useForm()
//     useEffect(()=>{
//         form.setFieldsValue({username:'defaunt name'})
//     })
//     const onFinishFill = (errors) => {
//         console.log(errors, '失败')
//     }
//     const onFinish = (values) => {
//         console.log(values, '成功')
//     }
//     return (
//         <div>
//             <h3>这是的表单</h3>
//             <Form form={form} onFinish={onFinish} onFinishFill={onFinishFill}>
//                 <Field name='username' rules={[{ required: true, message: '这可不能空' }]}>
//                     <Input placeholder="请输入密码" />
//                 </Field>
//                 <Field name='password' rules={[{ required: true, message: '这空这没事' }]}>
//                     <Input placeholder="请输入用户名" />
//                 </Field>
//                 <button>提交</button>
//             </Form>
//         </div>
//     )
// }


export default class FormPage extends Component {
    constructor(props) {
        super(props)
        this.formRef = React.createRef()
    }
    componentDidMount() {
        this.formRef.current.setFieldsValue({
            username: 'default value'
        })
    }
    onFinishFill = (errors) => {
        console.log(errors, '失败')
    }
    onFinish = (values) => {
        console.log(values, '成功')
    }
    render() {
        const { onFinish, onFinishFill, formRef } = this;
        return (
            <div>
                <h3>这是的表单</h3>
                {/* 函数组件不能使用hooks,这里有变化 */}
                {/*ref想在内部获取 react组件不能直接传递ref key 等属性,
                会被react使用并拦截,这要用到React.forwardRef api,
                把当前ref暴露给子组件,在Form lib中导出前
                    Form/index文件中可以看见包裹
                */}
                <Form ref={formRef} onFinish={onFinish} onFinishFill={onFinishFill}>
                    <Field name='username' rules={[{ required: true, message: '这可不能空' }]}>
                        <Input placeholder="请输入密码" />
                    </Field>
                    <Field name='password' rules={[{ required: true, message: '这空这没事' }]}>
                        <Input placeholder="请输入用户名" />
                    </Field>
                    <button>提交</button>
                </Form>
            </div>
        )
    }
}

Form/index.js 文件,这个文件没啥,就是导入一下导出,用了 一下React.forwardRef api

import React from 'react'
import _Form from './Form'
import Field from './Field'
import {useForm} from './useForm'

// 这里专门为了class组件 包裹,向下传递ref属性
const Form = React.forwardRef(_Form);

Form.Field = Field;
Form.useForm = useForm;
export {
    Field,
    useForm
}
export default Form;

Form 文件

import React from 'react'
import { FormProvider } from './context'
import { useForm } from './index'
export default function Form({ children, form, onFinish, onFinishFill }, ref) {
    // 这里调用useForm,获取咱们定义的api, 函数组件会在父组件直接获取,这里传如进去,复用上次创建的form
    const [formInstanc] = useForm(form);
    // useImperativeHandle这个api配合fowardRef,把子类的东西传递给父类
    React.useImperativeHandle(ref, () => ({ ...formInstanc, message: '我啊啊啊奥奥' }))
    return (
        <form onSubmit={(event) => {
            event.preventDefault();
        //    调用校验方法,返回promise,没错误就会成功,否则就是失败
            formInstanc.validator().then(res => {
                onFinish(res)
            }).catch((error) => {
                onFinishFill(error)
            })
        }}>
            {/* 利用context传如咱们定义的 api 存取校验等方法 */}
            <FormProvider value={formInstanc}>
                {children}
            </FormProvider>
        </form>
    )
}

Field.js 文件,基本算个消费者,接收并使用context中的方法

import React, { Component } from 'react'
import { FormContext } from './context'
export default class Field extends Component {
    static contextType = FormContext;
    constructor(props) {
        super(props)
        this.state = {}
    }
    // didmount中注册这个Item,
    componentDidMount() {
        const { register } = this.context;
        this.unRegister = register(this)
    }
    componentWillUnmount() {
        this.unRegister&&this.unRegister()
    }
    update = () => {
        // 强制更新
        this.forceUpdate()
    }
   
    // 完成双向数据绑定,与FormStore通信,直接从store中读取、存储
    getControlled = () => {
        const { name } = this.props
        const { getFieldValue, setFieldsValue } = this.context;
        return {
            value: getFieldValue(name),
            onChange: event => {
                setFieldsValue({ [name]: event.target.value })
            }
        }
    }
    render() {
        const { getControlled } = this;
        const { children } = this.props;
        const returnChild = React.cloneElement(children, getControlled())
        return returnChild

    }
}

useForm.js 提供存储数据,增改数据的方法

import { useRef } from 'react'
// formStore类,提供存储数据,增改数据的方法
class FormStore {
    constructor() {
        // 所有键值对
        this.store = {}
        // Form中的Item
        this.formItems = []
    }
    // 校验方法返回个promise对象
    validator = () => {

        return new Promise((resolve, reject) => {
            // 定义一个数组存错误
            let error = []
            // 遍历所有的FormItem组件,获取name和rules
            this.formItems.forEach(item => {
                const { name, rules } = item.props;
                // 这里意思意思,校验一下必填
                const rule = rules[0]
                if (rule.required && !this.store[name]) {
                    // 如果必填为true,对应值为空,加这个错误信息
                    error.push({ [name]: rule.message || '这个不能空' });
                }
            })
            // 有错reject错误信息
            if (error.length) {
                reject(error)
            } else {
                // 没错resolve数据
                resolve(this.store)
            }
        })
    }
    // 注册所有的FormItem组件
    register = (item) => {
        this.formItems.push(item)
        // 有注册有取消,返回一个取消注册方法在组件willunmount中用
        return () => {
            this.formItems = this.formItems.filter(formItem => formItem !== item);
        }
    }
    // 设置字段值,接收一个对象
    setFieldsValue = (newVal) => {
        this.store = {
            ...this.store,
            ...newVal
        }
        // 遍历对象,找出要更新的item组件执行更新
        Object.keys(newVal).forEach(name => {
            this.formItems.forEach(item => {
                // 每个formitem上接收用户传入的name属性,和当前改的name是一个 的话,就调用这个组件更新方法
                if (name === item.props.name) {
                    item.update()
                }
            })
        })
    }
    //接收一个表单名key值 获取对象值
    getFieldValue = (name) => {
        return this.store[name]
    }
    // 暴露给用户的方法
    getForm = () => {
        return {
            setFieldsValue: this.setFieldsValue,
            getFieldValue: this.getFieldValue,
            register: this.register,
            validator: this.validator
        }
    }
}

// 函数组件会执行两次useForm,所以要复用form
export function useForm(form) {

    const formRef = useRef()

    if (!form) {
        form = new FormStore().getForm()
    }
    formRef.current = form

    return [formRef.current]
}

Input.js

import React from "react";

const Input = props => {
  return <input {...props} />;
};

// const CustomizeInput = ({value = "", ...props}) => (
//   <div style={{padding: 10}}>
//     <Input style={{outline: "none"}} value={value} {...props} />
//   </div>
// );

class CustomizeInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }
  render() {
    const {value = "", ...otherProps} = this.props;
    return (
      <div style={{padding: 10}}>
        <Input style={{outline: "none"}} value={value} {...otherProps} />
      </div>
    );
  }
}

export default CustomizeInput;

context.js

import React from 'react'

export const FormContext=React.createContext()
export const FormProvider=FormContext.Provider
export const FormConsumer=FormContext.Consumer;

antd3简单实现

FormPage.js

import React, { Component } from 'react'
import Form from './../components/Form3'
import Input from './../components/Input'
class FormPage3 extends Component {
    handleSubmit = e => {
        e.preventDefault();
        this.props.form.validateFields((err, values) => {
          if (!err) {
            console.log('Received values of form: ', values);
          }else{
              console.log(err,'有错误')
          }
        });
      };
    render() {
        const { getFieldDecorator } = this.props.form;
        return (
            <div>
                <Form onSubmit={this.handleSubmit} >
                    <Form.Item>
                        {getFieldDecorator('username', {
                            rules: [{ required: true, message: 'Please input your username!' }],
                        })(
                            <Input

                                placeholder="Username"
                            />,
                        )}
                    </Form.Item>
                    <Form.Item>
                        {getFieldDecorator('password', {
                            rules: [{ required: true, message: 'Please input your Password!' }],
                        })(
                            <Input
                                type="password"
                                placeholder="Password"
                            />,
                        )}
                    </Form.Item>
                    <Form.Item>
                        <button type="primary"  >
                            Log in
          </button>

                    </Form.Item>
                </Form>
            </div>
        )
    }
}
export default Form.create()(FormPage3)

Form.js

import React from 'react'
//form咱啥也不干,form包裹一下
function Form(props) {
    return (
        <form {...props}>
            {props.children}
        </form>
    )

}
//Item咱啥也不干,form包裹一下,实际要展示校验信息的
function Item(props) {
    return props.children 
}
// create方法就是一个高阶函数
function create() {
    return (Com) => {
        return class extends React.Component {
            constructor(props) {
                super(props);
                this.state = {}
                this.options = {}
            }
            // 获取对应value
            getFieldValue = (name) => {
                return this.state[name];
            }
            // 设置value
            setFieldsValue = (newVal) => {
                this.setState({
                    ...this.state,
                    ...newVal
                })
            }
            // 校验
            validateFields = (callback) => {
                let error = []
                // 获取所有规则,遍历校验,有错就push
                Object.keys(this.options).forEach(item => {
                    const rule = this.options[item].rules[0]
                    if (rule.required && !this.getFieldValue(item)) {
                        error.push({ [item]: rule.message })
                    }
                })
                // 有错把错误反馈给form
                if (error.length) {
                    callback(error)
                } else {
                    // 没错把value反馈给form
                    callback(null, this.state)
                }
            }
            // 高阶函数,包裹input给它实现双向绑定,跟state通讯,直接改变state,直接更新
            getFieldDecorator = (name, rules) => {
                if (rules) {
                    this.options[name] = rules
                }
                return Com => {
                    const returnChild = React.cloneElement(Com, {
                        value: this.getFieldValue(name),
                        onChange: (event) => {
                            this.setFieldsValue({
                                [name]: event.target.value
                            })
                        }
                    })
                    return returnChild
                }
            }
            render() {
                const { props, getFieldDecorator, validateFields } = this;
                // form上暴露 api  子组件中this.props.form获取
                return <Com {...props} form={{ validateFields, getFieldDecorator }} ></Com>
            }
        }
    }
}


Form.create = create;
Form.Item = Item;
export {
    create,
    Item
}

export default Form;

Input.js和上面没变化