跳到主要内容

认识

2023年02月18日
柏拉文
越努力,越幸运

一、设计思路


  • 表单组件要求实现数据收集、校验、提交等特性,可通过⾼阶组件扩展
  • ⾼阶组件给表单组件传递⼀个input组件包装函数接管其输⼊事件并统⼀管理表单数据
  • ⾼阶组件给表单组件传递⼀个校验函数使其具备数据校验功能

二、实现目标


  1. 通过Form.create() 创建 Form
  2. getFieldDecorator:⽤于和表单进⾏双向绑定
  3. getFieldValue: 获取⼀个输⼊控件的值
  4. getFieldsValue: 获取⼀组输⼊控件的值,如不传⼊参数,则获取全部组件的值
  5. setFieldValue:
  6. validateFields: 校验并获取⼀组输⼊域的值与Error,若fieldNames参数为空,则校验全部组件

三、具体实现


细节
  1. 通过getFieldDecorator装饰Input组件时,为什么需要用React.cloneElement,直接用原来的Input组件可以吗?

答: 我们需要给Input组件添加valueonChange属性,但是我们又不能修改原来的组件,对原来的组件造成污染,所以我们需要一个全新的Input组件,所以需要用cloneElement实现

  1. 在设置Inputvalue字段值时,为什么需要给定默认值?

答: Reactinput中的value值为undefined时就变成了非受控组件,非受控组件是不可以通过onChange事件成为受控组件的,否则会报错。那么不让input变为非受控组件的解决方法是在给定value值时给一个默认值value=this.state[field]||""(默认值为空字符串,不可以是 undefined)

import React from "react";

function MyForm(props) {
return <div>{props.children}</div>;
}

MyForm.create = () => {
return (Comp) =>
class extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.options = {};
}
handleChange = (e) => {
const { name, value } = e.target;
this.setState({ [name]: value }, () => {
this.validateField(name);
});
};
getFieldValue = (field) => {
return this.state[field];
};
getFieldsValue = () => {
return { ...this.state };
};
validateField = (field) => {
const { rules = [] } = this.options[field];
const result = rules.some((rule) => {
if (rule.required) {
if (!this.state[field]) {
this.setState({ [field + "Message"]: rule.message });
return true;
}
}
return false;
});
if (!result) {
this.setState({ [field + "Message"]: "" });
}
return !result;
};
validateFields = (callback) => {
const validateResultList = Object.keys(this.options).map((field) => {
return this.validateField(field);
});
const result = validateResultList.every((rule) => rule);
callback(!result, { ...this.state });
};
getFieldDecorator = (field, options) => {
this.options[field] = options;
return (InputComp) => {
return (
<div>
{React.cloneElement(InputComp, {
name: field,
value: this.state[field] || "",
onChange: this.handleChange,
})}
{this.state[field + "Message"] && (
<div>{this.state[field + "Message"]}</div>
)}
</div>
);
};
};
render() {
return (
<Comp
{...this.props}
validateField={this.validateField}
getFieldValue={this.getFieldValue}
getFieldsValue={this.getFieldsValue}
validateFields={this.validateFields}
getFieldDecorator={this.getFieldDecorator}
/>
);
}
};
};

MyForm.Item = (props) => {
return <div>{props.children}</div>;
};

export default MyForm;
import React from "react";
import MyForm from "./MyForm";

function App(props) {
const { getFieldValue, getFieldsValue, getFieldDecorator, validateFields } =
props;
const handleLogin = () => {
console.log("获取 username 字段值", getFieldValue("username"));
console.log("获取全部字段值", getFieldsValue());
validateFields((error, value) => {
if (error) {
console.log("验证失败");
} else {
console.log("验证成功", value);
}
});
};
return (
<MyForm>
<h3>登录表单</h3>
<MyForm.Item>
<label>用户名:</label>
{getFieldDecorator("username", {
rules: [
{
required: true,
message: "请输入用户名",
},
],
})(<input name="username" type="text" placeholder="请输入用户名" />)}
</MyForm.Item>
<MyForm.Item>
<label>密码:</label>
{getFieldDecorator("password", {
rules: [
{
required: true,
message: "请输入密码",
},
],
})(<input name="password" type="password" placeholder="请输入密码" />)}
</MyForm.Item>
<MyForm.Item>
<button onClick={() => handleLogin()}>登录</button>
</MyForm.Item>
</MyForm>
);
}

export default MyForm.create()(App);