antd form.create()表單數(shù)據(jù)綁定
實(shí)現(xiàn)的主要原理:利用高階組件對(duì)原組件props屬性方法再封裝,可以理解為組件/函數(shù)方法中傳參為組件,外層可嵌套多層函數(shù)或組件;
創(chuàng)建組件類
/**
* 模擬antd下的Form下掛載create方法
* 使用方法Form.create(component) 或 @Form.create class Dmo extent Component{}
*/
class Form extends React.PureComponent { }
Es6 @可修飾類和類里面的屬性,無(wú)法修飾函數(shù):
- 修飾器只能用于類和類的方法,不能用于修飾函數(shù),因?yàn)榇嬖诤瘮?shù)提升。
- 由于存在函數(shù)提升,使得修飾器不能用于函數(shù)。類是不會(huì)提升的,所以就沒(méi)有這方面的問(wèn) 題。
高階組件屬性代理
import React from "react";
import fieldStore from "./fieldStore";
import { comparisonObject } from "./utils/common";
Form.create = (option = {}) => {
const {
scrollIntoView, // 表單提交異常時(shí)滾動(dòng)表單項(xiàng)到視圖中央
callback_onErr, // 接收表單異常處理方法
} = option;
return (WrappedComponent) => {
return class extends React.Component {
constructor() {
super();
this.state = {};
this.fieldStore = fieldStore()
}
/** 表單容器 */
getFieldDecorator = (key, config = { rules: [], initialValue }) => {
if (!key) throw Error("getFieldDecorator Error: lost formItem key");
let rules = this.fieldStore.rules;
let store = this.fieldStore.store;
let initialMata = this.fieldStore.initialMata;
if (rules[key] === undefined || (rules[key].length && !comparisonObject(rules[key][0], config.rules[0]))) {
this.fieldStore.dispatchRules(key, config.rules);
}
if (!(key in initialMata)) {
this.fieldStore.dispatchInitialMeta(key, config.initialValue);
}
return (ComponentF) => {
const onChange = (v) => {
if (ComponentF.props.onChange) ComponentF.props.onChange(v);
const type = Object.prototype.toString.call(v);
this.fieldStore.dispatchStore(key, type === "[object Object]" ? (v.target.value || e.target.checked) : v);
this.forceUpdate(); // 強(qiáng)制屬性刷新
}
const propsNew = {
...ComponentF.props,
onChange: e => onChange(e),
defaultValue: initialMata[key] || ComponentF.value,
}
let EleType = ComponentF.type.name;
if (typeof ComponentF.type === 'function') propsNew.value = store[key] || initialMata[key] || ComponentF.value;
if (EleType === "Switch") propsNew.checked = store[key] || initialMata[key] || ComponentF.checked;
if (scrollIntoView) {
propsNew.id = `${key}ByGetFieldDecorator`;
}
const newTree = React.cloneElement(ComponentF, propsNew, ComponentF.props.children);
return newTree;
}
}
validateFieldsWraped = (submit) => {
if (this.state.submitting) return;
this.fieldStore.validateFields((errMess, store) => {
if (scrollIntoView) {
return document.querySelector(`#${errKey}ByGetFieldDecorator`).scrollIntoView({ block: "center", behavior: "smooth" });
}
if (callback_onErr && errMess) {
return callback_onErr(errMess);
}
if (errMess) return console.error(errMessage);
this.setState({ submitting: false })
submit(errMess, store);
})
}
submitting = () => {
return Boolean(this.state.submitting)
}
addtionaProps = () => {
return {
getFieldDecorator: this.getFieldDecorator,
setFieldsValue: this.fieldStore.setFieldsValue,
getFieldValue: this.fieldStore.getFieldValue,
validateFields: this.validateFieldsWraped,
resetFields: this.fieldStore.resetFields,
store: this.fieldStore.store,
submitting: this.submitting(),
}
}
render() {
var props = { form: this.addtionaProps() };
return (
<WrappedComponent
{...this.props}
{...props}
/>
)
}
}
}
}
fieldStore
/**
* 表單倉(cāng)庫(kù)
*/
class FieldStore {
constructor(fields) {
this.store = { ...fields };
this.rules = {};
this.initialMata = {};
}
dispatchStore = (key, value) => {
this.store[key] = value;
}
dispatchInitialMeta = (key, value) => {
this.initialMata[key] = value;
}
dispatchRules = (key, rule) => {
this.rules[key] = rule;
}
getFieldValue = (key) => {
return this.store[key] || this.initialMata[key];
}
setFieldsValue = (props) => {
this.store = {
...this.store,
...props,
}
}
resetFields = () => {
this.store = { ...fields };
this.rules = {};
this.initialMata = {};
}
validateFields = (submitting) => {
let errFlag = false,
errMessage = "",
errKey = "",
rules = this.rules;
let store = Object.assign(this.initialMata, this.store);
for (let i = 0; i < Object.keys(store).length; i++) {
let item = store[Object.keys(store)[i]];
if (rules && rules[Object.keys(store)[i]]) {
let r = rules[Object.keys(store)[i]];
if (r && r.length && r[0].required && (!item && item !== 0 && item !== false)) {
errFlag = true;
errMessage = r[0].message || "表單填寫不完全";
errKey = Object.keys(store)[i];
} else if (r.length === 2 && r[1].validator) { // 自定義校驗(yàn)可正則
function call(mess) {
if (mess) {
errMessage = mess || "表單項(xiàng)格式不正確";
errFlag = true;
errKey = Object.keys(store)[i];
}
}
// 傳入表單值、回調(diào)函數(shù), 回調(diào)函數(shù)是否返回errMessage控制
try {
r[1].validator(item, call);
} catch (error) {
errMessage = error;
}
}
if (errMessage) break;
}
}
submitting(errMessage, store);
}
}
export default function createFieldsStore(fields) {
return new FieldStore(fields);
}
使用
import { Button, Toast, Picker, List } from "antd-mobile";
import Form from "./components/Form";
import React from "react";
@Form.create({
callback_onErr: (mess) => {
console.log(mess)
Toast.fail(mess)
}
})
class Demo extends React.Component {
submit = () => {
const { form: { validateFields } } = this.props;
validateFields((err, fieldsValue) => {
if (err) return;
console.log(fieldsValue, "表單數(shù)據(jù)集合》》》》》》》》》》》》》》》》》》》")
})
}
render() {
const { form: { getFieldDecorator, getFieldValue, store } } = this.props;
return (
<div>
<div>
{
getFieldDecorator("a", {
initialValue: [1],
rules: [{ required: true, message: "手機(jī)號(hào)不能為空" }],
})(
<Picker
data={[{ label: "是", value: 2 }, { label: "是213", value: 1 }]}
cols={1}
>
<List.Item arrow="horizontal"></List.Item>
</Picker>
)
}
</div>
<div>
{
getFieldValue('a') == 1 ? getFieldDecorator("bbbbbbbbb", {
initialValue: 'bbbbbbbbb',
rules: [{ required: true, message: "手機(jī)號(hào)不能為空" }],
})(
<input placeholder="請(qǐng)輸入手機(jī)號(hào)" />
) : ''
}
</div>
<div>
{
getFieldValue('a') == 2 ? getFieldDecorator("ccccccccc", {
initialValue: 'ccccccccc',
rules: [{ required: true, message: "手機(jī)號(hào)不能為空" }],
})(
<input placeholder="請(qǐng)輸入手機(jī)號(hào)" />
) : ''
}
</div>
<Button onClick={this.submit}>提交</Button>
</div>
)
}
}