and3-form的使用以及核心代码编写
and3-form的使用
import React, { Component } from 'react';
import { createForm } from "rc-form";
import Input from '../components/Input'
const nameRule = {
required: true,
message: '名称必输'
}
const passwordRule = {
required: true,
message: '名称必输'
}
class RcFormPage extends Component {
componentDidMount() {
console.log('props', this.props)
}
submit = () => {
const { getFieldsValue,validateFields } = this.props.form;
validateFields((err,value) => {
console.log(err,value)
})
}
render() {
const { getFieldDecorator } = this.props.form;
return (
<div>
<h3>RcFormPage</h3>
{getFieldDecorator('name', {
rules: [nameRule]
})(<Input placeholder="请输入名称"/>)}
{getFieldDecorator('password', {
rules: [passwordRule]
})(<Input placeholder="请输入密码"/>)}
<div>
<button onClick={this.submit}>submit</button>
</div>
</div>
);
}
}
export default createForm()(RcFormPage);
antd3表单组件设计思路
- 表单组件需要实现数据收集、校验、提交等特性,通过高阶组件扩展(接收一个组件,给组件添加form属性,返回一个新的组件)
- 高阶组件给表单组件传递一个input组件包装函数接管其输入时间并统一管理表单数据
- 高阶组件给表单组件传递一个校验函数使其具备数据校验功能
antd3的设计有个问题,局部的变化回引起整体的变化,因为state是保存到form里的,所以setState的时候所有的组件都会更新。
手写ant3-form核心代码
import React, { Component } from 'react';
function createForm(options) {
return (Cmp) => {
return class extends Component {
state = {}
options = {}
getForm = () => {
return {
getFieldsValue: this.getFieldsValue,
getFieldValue: this.getFieldValue,
getFieldDecorator: this.getFieldDecorator,
setFieldValue: this.setFieldValue,
validateField:this.validateField
}
}
handleChange = (e) => {
const { value, name } = e.target;
this.setState({
[name]: value
})
}
getFieldDecorator = (fieldName, option) => {
this.options[fieldName] = option;
return (InputCmp) => {
return React.cloneElement(InputCmp, {
name: fieldName,
value: this.state[fieldName] || '',
onChange: this.handleChange
})
}
}
validateField = (callback) => {
let err = [];
for (const fieldName in this.options) {
const rules = this.options[fieldName].rules;
if(rules.length>0){
for (let i = 0; i < rules.length; i++) {
if(rules[i].required && this.state[fieldName] === undefined){
err.push({[fieldName]:rules[i].message})
}
}
}
}
if(err.length>0){
callback(err,this.state)
}else{
callback(null,this.state)
}
}
getFieldsValue = () => {
return this.state;
}
getFieldValue = (name) => {
return this.state[name]
}
setFieldValue = (newStore) => {
this.setState(newStore)
}
render() {
const form = this.getForm();
return <Cmp {...this.props} form={form}/>
}
}
}
}
export { createForm };
and4-form的使用以及核心代码编写
and4-form的使用
import React, { useEffect } from "react";
import { Form, Button, Input } from "antd";
const FormItem = Form.Item;
const nameRules = { required: true, message: "请输入姓名" };
const passwordRules = { required: true, message: "请输入密码" };
const AntdFormPage = () => {
const [form] = Form.useForm();
const onFinish = (val) => {
console.log("onFinish", val);
};
const onFinishFailed = (val) => {
console.log("onFinishFailed", val);
};
useEffect(() => {
form.setFieldsValue({ username: "张三" });
}, []);
return (
<div>
<h3>AntdFormPage</h3>
<Form form={form} onFinish={onFinish} onFinishFailed={onFinishFailed}>
<FormItem name="username" label="姓名" rules={[nameRules]}>
<Input placeholder="请输入名字" />
</FormItem>
<FormItem name="password" label="密码" rules={[passwordRules]}>
<Input placeholder="请输入密码" />
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit">
Submit
</Button>
</FormItem>
</Form>
</div>
);
};
export default AntdFormPage;
antd4表单实现思路
- 创建一个formStore,将所有的数据都存到store里,并提供get,set方法
- 通过发布订阅的方式,在修改了formStore中的值之后执行forceUpdate,避免刷新所有组件
index.js
import React from "react";
import Field from "./Field";
import _Form from "./Form";
import useForm from "./useForm";
const Form = React.forwardRef(_Form);
Form.Field = Field;
Form.useForm = useForm;
export { Field, useForm };
export default Form;
Form.js
import React from 'react';
import FieldContext from "./FieldContext";
import useForm from "./useForm";
function Form({ form, children, onFinish, onFinishFailed },ref) {
const [formInstance] = useForm(form);
formInstance.setCallbacks({ onFinish, onFinishFailed })
React.useImperativeHandle(ref,()=>formInstance);
return (
<form onSubmit={(e)=>{
e.preventDefault();
formInstance.onSubmit();
}}>
<FieldContext.Provider value={formInstance}>
{children}
</FieldContext.Provider>
</form>
);
}
export default Form;
Field.js
import React, { Component } from 'react';
import FieldContext from "./FieldContext";
class Field extends Component {
static contextType = FieldContext;
componentDidMount() {
this.unRegisterFieldEntities = this.context.registerFieldEntities(this);
}
componentWillUnmount() {
this.unRegisterFieldEntities();
}
onStoreChange = () => {
this.forceUpdate();
}
getControlled = () => {
const { getFieldValue, setFieldsValue } = this.context;
const { name } = this.props;
return {
value: getFieldValue(name),
onChange: (e) => {
const newValue = e.target.value;
setFieldsValue({ [name]: newValue });
}
}
}
render() {
console.log(1)
const { children } = this.props;
const cloneNode = React.cloneElement(children, this.getControlled())
return (
cloneNode
);
}
}
export default Field;
useForm.js
import { useRef } from "react";
class FormStore {
constructor() {
this.store = {};
this.fieldEntities = []
this.callbacks = {}
}
setCallbacks = (callback) => {
this.callbacks = {
...this.callbacks,
...callback
}
}
registerFieldEntities = (field) => {
this.fieldEntities.push(field);
return () => {
this.fieldEntities.filter((item) => {
return item !== field
})
delete this.store[field.props.name]
}
}
getFieldValue = (fieldName) => {
return this.store[fieldName];
}
getFieldsValue = () => {
return { ...this.store }
}
setFieldsValue = (newStore) => {
this.store = {
...this.store,
...newStore
}
this.fieldEntities.forEach((f) => {
Object.keys(newStore).forEach((key) => {
if(f.props.name === key){
f.onStoreChange();
}
})
})
console.log('store',this.store)
}
validate = () => {
let err = []
this.fieldEntities.forEach((field) => {
const { rules,name } = field.props;
const rule = rules && rules[0];
const value = this.getFieldValue(name);
if(rule && rule.required && (value == undefined || value === '')){
err.push({
[name]:rule.message,
value
})
}
})
return err;
}
onSubmit = () => {
const err = this.validate();
if(err.length === 0){
this.callbacks.onFinish(this.store);
}else{
this.callbacks.onFinishFailed(err,this.store);
}
}
getForm = () => {
return {
getFieldValue: this.getFieldValue,
getFieldsValue: this.getFieldsValue,
setFieldsValue: this.setFieldsValue,
registerFieldEntities: this.registerFieldEntities,
setCallbacks:this.setCallbacks,
onSubmit:this.onSubmit
}
}
}
export default function useForm(form) {
const formRef = useRef();
if (!formRef.current) {
if (form) {
formRef.current = form;
}else{
const formStore = new FormStore();
formRef.current = formStore.getForm();
}
}
return [formRef.current];
}
FieldContext.js
import React from 'react'
const FieldContext = React.createContext();
export default FieldContext;