Article content and code based on ANTD4.x, the end of the article attached source code, with the source code reading effect is better. Read this article and you will learn one thing. The core principles of Form. 2. Write a simple Form by hand
Antd Form core principles
In antD Forms, there is a Store Store above all Field components for storing Form values. Implement bidirectional binding of Store to component values by passing Value and onChange to child components of Field. When the Store Value changes, the Value of the Field corresponding to the changed name follows the change and triggers the forceUpdata of the Field to update the view.
Field injection of three parameters
As we know, FormItem by default puts three parameters in the Props property of our component: ID, value, and onChange. To understand how form works, you need to understand these three parameters.
1.id
<Form.Item
name='name'
rules={[{ required: true, message: 'Missing first name' }]}
>
<Form.Item
name={['name','firstName']}
rules={[{ required: true, message: 'Missing first name' }]}
>
<MyInput></MyInput>
</Form.Item>
</Form.Item>
Copy the code
As we nested the FormItem above, we ended up with the id ‘name_firstName’ in the props of our custom component MyInput. The ID attribute is not used in general scenarios. In some special cases, we can parse the ID to get the pathName of the current field for subsequent operations.
2.value
When the form Store data changes, the form passes a new Value to the Field corresponding to the namePath to update the Value of the child component.
3.onChange(trigger)
The default value of the trigger is onChange. Triggers that listen for changes in the child component and modify the Store value when the child component value changes.
2. Handwritten simple Form
Based on the above principle, we can deepen our understanding by implementing a simple version of Form, removing the functions such as List, multi-layer field nesting and verification function, and realizing a two-way response of Store and field Value.
UseForm
Let Store: store = {} const fieldEntities: // notifyObservers(namePath) // when the store is about to change, Notify corresponding field forceUpdata setFieldsValue(newValue) // Full assignment Store registerField(entity) // Each field instantiation is registered into fieldEntities. Dispatch ({type: 'updateValue', namePath: 'xx', value: 'newFieldValue'}) useForm(form) // Bind form hookCopy the code
UseForm declares a global data warehouse store for the form and a list of field instances, fieldEntities. Provides store warehouse add, delete, change check function, store warehouse is operated through bian libianli
FieldContext
const context = React.createContext<FormInstance>({
getFieldsValue: warningFunc,
setFieldsValue: warningFunc,
registerField: warningFunc,
dispatch: warningFunc,
})
export default context
Copy the code
Context properties that apply globally to the form.
- GetFieldsValue Obtains the full Store
function getFieldsValue(){
let result: Store = {}
fieldEntities.forEach((field:any)=>{
const name = field.name
result[name] = store[name]?store[name]: undefined
})
return result
}
Copy the code
The return values are supplemented from the list of field instances
- SetFieldsValue Sets the full Store
function setFieldsValue(newValue: Store){
fieldEntities.forEach((entity)=>{
entity.onStoreChange()
})
return store = newValue
}
Copy the code
Traversing the instance list notifies Field forceUpdata
- UpdateValue updates the value of a specific name
function updateValue(namePath:any, value:any){
store[namePath] = value
notifyObservers(namePath)
}
Copy the code
Each time the value changes, the field is notified to update
Field
const returnChildNode = React.cloneElement(
children,
getControlled(children.props)
);
return <React.Fragment>{returnChildNode}</React.Fragment>;
Copy the code
In the Field, you need to inject Value and onChange to the props property of the subcomponent.
- getControlled
function getControlled(childProps: Record<string, any>) { const formValue = getFieldsValue(); const mergedGetValueProps = { value: formValue[name], }; const control: Record<string, any> = { ... childProps, ... mergedGetValueProps, }; control[trigger] = (... args: any[]) => { let eventValue = defaultGetValueFromEvent("value", ... args); dispatch({ type: "updateValue", namePath: name, value: eventValue, }); }; return control; }Copy the code
Overrides the component’s value and onChange properties with structural assignment. Listen on the trigger of the Field child to update the changes to the Store.
Form
function Form({ children, form }: FormProps) {
const [formInstance] = useForm(form);
const formContextValue: FormInstance = formInstance;
const wrapperNode = (
<FieldContext.Provider value={formContextValue}>
{children}
</FieldContext.Provider>
);
return <div>{wrapperNode}</div>;
}
Copy the code
Bind a hook to a Form. A Form hook corresponds to a Form instance.
summary
Antd Form uses forceUpdata to implement two-way updates between Store and Field, bypassing react state. The Form instance then provides a small number of manipulation functions, but enough for developers to use, while namPath’s array Settings are suitable for more complex scenarios.
In order to make it easier for readers to understand, this implementation removes many of Antd Form’s core functions, such as FormList and validator. Interested students can refer to its source code.
Refer to the link
Github.com/react-compo…
Source warehouse
Github.com/justworkhar…