React Form表单的简单实现

时间:2021-1-8 作者:admin

先来捋一捋表单的实现思路吧!

  1. 阅读antd这部分源码之后,发现antd3和antd4在表单的实现思路还是有很大的差异的。
  • antd3的设计思路是需要实现表单数据的收集、校验、提交等特性。通过使用高阶组件进行扩展。高阶组件给表单组件传递一个input组件的包装函数用于接管input组件的输入事件,并统一管理表单数据。关于校验功能的实现是高阶组件给表单传递一个校验函数使其具备数据检验的功能。
  • antd4是基于rc-field-form实现的.在数据管理方面与antd3有很大的不同

! antd3将需要管理的数据存在form的state中,这样会带来很大的渲染消耗,当某一个子组件的状态发生了变化,form表单下所有的组件都会被重新渲染。 antd4则将数据存放在一个store中进行统一管理,某一组价状态的更新不会造成全部组件的重新渲染

  1. 基于rc-field-form实现Form表单的准备工作
  • 执行 yarn add rc-filed-form;
  • 了解rc-field-form的基本使用
  • 自己实现my-field-form
  1. 目录结构
  • input.js
  • index.js –入口文件
  • Form.js
  • Field.js
  • useForm.js
  • context.js –通过Context来跨层级传递数据
  1. rc-field-form 基本使用
import React from 'react';
// import Form, { Field } from 'rc-field-form';
import Form, { Field } from '../../components/my-field-form';
import Input from '../../../components/input';
import './index.less';

class RcFormUse extends Component{
    formRef = React.createRef();
    componentDidMount() {
        console.log("form", this.formRef.current); 
        // this.formRef.current.setFieldsValue({username: "default"});
    }
    onFinish = val => {
        console.log("onFinish", val); 
    };
    // 表单校验失败执行
    onFinishFailed = val => {
        console.log("onFinishFailed", val);
    };
    render () {
        return (
            <div>
                <h3>RcFormUse</h3>
                <Form
                ref={this.formRef}
                onFinish={this.onFinish} 
                onFinishFailed={this.onFinishFailed}
                >
                    <Field name="username" rules={[{required: true, message: "请输入姓名!"}]}>
                        <Input placeholder="Username" />
                    </Field>
                    <Field name="password" rules={[{required: true, message: "请输入密码!"}]}>
                        <Input placeholder="Password" />
                    </Field>
                    <button>提交</button>
                </Form>
            </div>
        )
    }
}

export default RcFormUse;
  1. Input 组件封装
import React from 'react';

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

const CustomInput = (props) => {
    const {value, otherProps} = props;
    return <Input style={{outline: 'none'} value={value} {...otherProps}></Input>
}

export default CustomInput;
  1. index.js 入口文件
import React from 'react';
import _Form from './Form';
import Field from './Field';
import useForm from './useForm';

// 使用forwardRef转发Form组件的返回form的ref
const Form = React.forwardRef(_Form);
const Form.useForm = useForm;

exprot { Field };
export default Form;
  1. Context.js
import React from 'react';

// 创建一个Context对象,跨层级穿递数据
const FieldContex = React.creacteContext;

export default FieldContext;
  1. useForm.js
import React from 'react';

// 使用FormStore存储收集的数据
class FormStore {
    constructor() {
        this.store = {};
        // 注册子组件,用于强制更新子组件,与源码不同,源码中使用的是数组
        this.fieldEntities = {};
    }
    // 注册组件,用于组件更新
    registerEntity = entity => {
        console.log('注册的filed组件:', entity);
        this.fieldEntities = {
            ...this.fieldEntities,
            [entity.props.name]: entity
        }

        // 取消组件注册
        return () => {
            delete this.fieldEntities[entity.props.name];
        }
    }
    getFieldVal = (name) => {
        const v = this.stroe[name];
        return v;   
    }
    setFieldVal = (newStore) => {
        this.store ={
            ...this.store,
            ...newStore
        }

        // 更新field组件,执行强制更新
        Object.keys(newStore).forEach(name => {
            this.fieldEntities[name].onStoreChange();
        })
    }
    // 统一向外暴露方法
    getForm() {
        return {
            getFieldVal: this.getFieldVal,
            setFieldVal: this.setFieldVal,
            registerEntity: this.registerEntity
        }
    }
}

// 自定义组件
const useForm = (form) => {
    const formRef = React.useRef();
    if (!formRef.current) {
      if (form) {
        formRef.current = form;
      } else {
        const formStore = new FormStore();
        formRef.current = formStore.getForm();
      }
    }
    return [formRef.current];
}

export default useForm;
  1. Form.js
import React from 'react';
import useForm from './useForm';
import FieldContext from './FiledContext';

const Form = (props) => {
    const [formInstance] = useForm();
    console.log('formInstance:', formInstance);
    return (
        <form>
            <FieldContext.Provider value={formInstance}>
                {props.children}
            </FieldContext.Provider>
        </form>
    )
}

export default Form;
  1. Field.js
import React from 'react';
import FieldContext from './FiledContext';

class Field extends React.Component{
    static contextType = FieldContext;
    componentDidMount () {
        // 在context中添加注册当前对象
        this.unregisterEntity = this.context.registerEntity(this);
    }
    componentWillUnmount () {
        // 取消订阅
        if(this.unregisterEntity) {
            this.unregisterEntity();
        }
    }
    // 执行强制更新
    onStoreChange() {
        this.forceUpdate();
    }
    getCntrolled = () => {
        const { getFieldVal, setFieldVal } = this.context;
        const { name } = this.props;
        return {
            value: getFieldVal(name),
            onChange: (e) => {
                const newVal = e.target.value
                setFieldVal({[ name ]: newVal});
                console.log('值:', newVal);
            }
        }
    }
    render(){
        const { children } = this.props;
        return React.cloneElement(children, this.getCntrolled())
    }
}

export default Field;
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。