form表单大概有几个重要的组件
Form 主要是负责提交信息
Field 每一个输入选项包裹-- 主要是 给 包裹对象增加额外的处理
useForm 通过自定义组件来处理所有逻辑
Form 和 下面的children 可以通过 context 传递数据
Form -- 最外层的表单
- 处理sumbit方法
- 使用自定义hook-useForm
- 创建Provider 传递 context
const [ formInstance ] = useForm(form)
formInstance.registerCallback({
onFinish,
onFinishFailed
})
return (
<form
onSubmit={event => {
event.preventDefault()
formInstance.submit()
}}
>
<FieldContext.Provider value = { formInstance }>//把逻辑传递下去
{ children }
</FieldContext.Provider>
</form>
)
Field
- 赋予表单的功能,以input为例,需要赋予value 属性,onChanges事件 因为赋予功能,需要用到
React.cloneElement这个api - 要考虑到当获取的数据变化同样UI要更新,所有在onChange改变数据之后,
forceUpdate一下
render() {
const { children } = this.props;
const returnChildNode = React.cloneElement(children, this.getControlled())
return returnChildNode
}
getControlled(){
const { getFieldValue,setFieldsValue } = this.context;
const { name } = this.props
return {
value: getFieldValue(name),
onChange: (event)=>{
let newVal = event.target.value;
setFieldsValue({
[name]: newVal
})
}
}
}
onStoreChange(){
this.forceUpdate()
}
最后就是所有的逻辑控制层,自定义hook useForm
- 设置 store 存储数据 和 组件的实例
- 设置一些方法 getFieldsValue setFieldsValue submit、validate等
- 最后定义useForm
定义数据
class FormStore {
constructor(){
this.store = {} // { "username":"lilei","password":"123" }各种表单项
this.fieldEntities = []
this.callbacks = {}
}
定义方法
getFieldValue = (name) => {
return this.store[name]
}
getFiledsValue(){
return this.store
}
setFieldsValue = (newStore) => {
this.store = {
...this.store,
...newStore
}
console.log("this.fieldEntities",this.fieldEntities)
this.fieldEntities.forEach( entity => {
const { name } = entity.props;
Object.keys(newStore).map( key => {
if(key === name){
entity.onStoreChange()
}
} )
} )
}
定义useForm
export default function useForm(form) {
const formRef = useRef()
console.log(formRef.current)
//特别重要不然就不是维护同一个 FormStore
if(!formRef.current){
if(form){
formRef.current = form
}else{
const formStore = new FormStore();
formRef.current = formStore.getForm()
}
}
return [formRef.current]
}
总结:useForm 是通过useRef() 的 current 来构建单例模式,Form->Field->Input 数据采用 context, 组件调用useForm逻辑,先把组件注册进去,然后在useForm 里 调用 组件的方法。