FormStore类保存数据
- 状态不保存在共同祖先form组件里,而是保存在第三方store里。原因是因为第一种情况当某个value更新时,会更新form的所有子组件。保存第三方store在value更新时找到对应的组件实例更新即可。
- store保存表单数据,fieldEntities保存field组件实例,callbacks保存submit后回调方法
class FormStore {
constructor() {
this.store = {};
this.fieldEntities = [];
this.callbacks = {};
}
setCallbacks = (newCallbacks) => {
this.callbacks = {
...this.callbacks,
...newCallbacks,
};
};
registerField = (field) => {
this.fieldEntities.push(field);
return () => {
this.fieldEntities = this.fieldEntities.filter((_f) => _f !== field);
delete this.store[field.props.name];
};
};
getFieldValue = (name) => {
return this.store[name];
};
getFieldsValue = () => {
return {...this.store};
};
setFieldsValue = (newStore) => {
this.store = {
...this.store,
...newStore,
};
this.fieldEntities.forEach((field) => {
Object.keys(newStore).forEach((k) => {
if (k === field.props.name) {
field.onStoreChange();
}
});
});
};
validate = () => {
let err = [];
return err;
};
submit = () => {
let err = this.validate();
if (err.length > 0) {
this.callbacks.onFinishFailed(err, this.getFieldsValue());
} else {
this.callbacks.onFinish(this.getFieldsValue());
}
};
getForm = () => {
return {
getFieldValue: this.getFieldValue,
getFieldsValue: this.getFieldsValue,
setFieldsValue: this.setFieldsValue,
registerField: this.registerField,
submit: this.submit,
setCallbacks: this.setCallbacks,
};
};
}
form组件
- 创建一个FormStore实列,使改组件有操作数据,校验数据,更新组件等的能力
- 把FormStore实列通过context共享给子组件field
- 用useRef保存 实例,保证组件更新不影响store
import React from "react";
import useForm from "./useForm";
import FieldContext from "./FieldContext";
export default function Form({children, form, onFinish, onFinishFailed}, ref) {
const [formInstance] = useForm(form);
React.useImperativeHandle(ref, () => formInstance);
formInstance.setCallbacks({
onFinish,
onFinishFailed,
});
return (
<form
onSubmit={(e) => {
e.preventDefault();
formInstance.submit();
}}>
<FieldContext.Provider value={formInstance}>
{children}
</FieldContext.Provider>
</form>
);
}
field组件
- 通过context获取form组件创建的FormStore实例,给子组件注入value和onchange等属性
- 再渲染完成,准备销毁阶段通知store注册,销毁该组件实例
- 声明onStoreChange更新该组件的方法,让获取field组件实例的store可以在值变更时更新该组件
import React, {Component} from "react";
import FieldContext from "./FieldContext";
export default class Field extends Component {
static contextType = FieldContext;
componentDidMount() {
this.unregisterField = this.context.registerField(this);
}
componentWillUnmount() {
this.unregisterField();
}
onStoreChange = () => {
this.forceUpdate();
};
getControlled = () => {
const {name} = this.props;
return {
value: this.context.getFieldValue(name),
onChange: (e) => {
const newValue = e.target.value;
this.context.setFieldsValue({[name]: newValue});
},
};
};
render() {
console.log("render");
const returnChildNode = React.cloneElement(
this.props.children,
this.getControlled()
);
return returnChildNode;
}
}