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;
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和上面没变化