One, foreword

When I was developing today, I realized that the form on one page is actually two parts, one of which is referenced on another page. This increases the amount of code, so optimize, in order to reuse the form here.

As shown in the figure, the requirement is to separate the following two forms, which can be reused. In fact, there may be many forms in the two parts, but only three are written here for convenience.

Two, involving knowledge points

  • react hooks
  • Antd 4.0 (Form)
  • Promise
  • Child parent component passes values and parent component calls child component methods

Third, implementation method

Concrete implementation: I put in Codesandbox, can directly open debugging

  • 1. First of all, in order to fully reuse form components, it is necessary to split them thoroughly and not associate too much content.
  • 2. A template form is created as a component by useForm().
  • 3. Submit the form outside the component, trigger it separately, and get the form data via the promise.all method.
  • 4. When editing, you can assign values to the form via props, and then register methods to the child component for the parent component to call.

Four, specific operation

Let’s experiment with codesandBox, where the form is added less and can be added by itself.

Create a new index.js file where the form ends up

Two form templates: templateForm1.js TemplateForm2.js. Since the contents of these two files are basically the same, we need to define them according to the content of the form, so we write one here.

import React, { useRef } from "react"; import { Button, Divider } from "antd"; import TemplateForm1 from "./templateForm1"; import TemplateForm2 from "./templateForm2"; export default function App() { const template1Ref = useRef(); const template2Ref = useRef(); const formItemLayout = { labelCol: { span: 4 }, wrapperCol: { span: 10 } }; const handleCommit = () => { let template1Data = new Promise((resolve, reject) => { template1Ref.current.commitForm((value) => { resolve(value); }); }); let template2Data = new Promise((resolve, reject) => { template2Ref.current.commitForm((value) => { resolve(value); }); }); Promise.all([template1Data, template2Data]).then((res) => { console.log("get", res); }); }; Return (<div className="App"> <Divider orientation="left"> Internal information </Divider> <TemplateForm1 ref={template1Ref} FormItemLayout ={formItemLayout} /> <Divider orientation="left"> External information </Divider> <TemplateForm2 ref={template2Ref} FormItemLayout ={formItemLayout} /> <Button onClick={handleCommit}> Submit </Button> </div>); }Copy the code

In this file, we introduce templates for two forms, getting components through the REF attribute and passing variables for the parent component through props.

When we call handleCommit, the function calls the commitForm method defined by the child component to get the form’s data, which is returned via the callback function.

Because of the problem of asynchronously fetching multiple components, we use the promise.all method to fetch the contents of multiple forms and process them together.

import React, { useImperativeHandle, forwardRef } from "react"; import { Form, Input } from "antd"; const TemplateForm1 = (props, ref) => { const { formItemLayout } = props; const [form] = Form.useForm(); useImperativeHandle(ref, () => ({ commitForm: (cb) => { handleCommit(cb); }})); const handleCommit = async (cb) => { try { const values = await form.validateFields(); cb(values); } catch (err) { console.log(err); }}; return ( <Form form={form} {... FormItemLayout}> < form. Item name=" name "label=" name" rules={[{required: true, message: "Please enter a name}]}" > < Input placeholder = "point Input name" / > < / Form Item > < / Form >). }; export default forwardRef(TemplateForm1);Copy the code

This component is a form template that we define. After normal reading, we get the form content via async and await, and finally return it to the parent component via commitForm methods.