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…