This is the 17th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021
How do I write popovers quickly
What would you say if you were asked to click the New button, pop up a form and submit it?
It’s easy, 60 or 70 lines of code.
import { Button, Form, Input, Modal, Select } from 'antd';
import './style.less';
import { useState } from 'react';
export default function TestPage() {
const [visiable, setVisiable] = useState(false);
const [form] = Form.useForm();
// Open the popover
const open = () = > {
setVisiable(true);
};
// Close the popover
const close = () = > {
setVisiable(false);
};
// Click OK to submit form
const submit = () = >{
form.submit()
}
// Get the form data after submission, request the interface, reset the form and close
const onSubmit = (values) = >{
console.log(values)
//await fetch ...
form.resetFields();
close()
}
return (
<div>
<div className="text-center">
<Button type="primary" onClick={open}>new</Button>
</div>
<Modal
wrapClassName="modal-wrap"
okText="Submit"
cancelButtonProps={{ shape: 'round'}}okButtonProps={{ shape: 'round'}}width={600}
visible={visiable}
title="New User"
onCancel={close}
onOk={submit}
>
<div className="form">
<Form form={form} labelCol={{ span: 4 }} wrapperCol={{ span: 16 }} onFinish={onSubmit}>
<Form.Item
label="Username"
name="username"
rules={[{ required: true.message: 'Please input username!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="User email"
name="mail"
rules={[{ required: true.message: 'Please input mail!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="Department"
name="depart"
rules={[{ required: true.message: 'Please input depart!' }]}
>
<Select>
<Select.Option value={1}>The Marketing Department</Select.Option>
<Select.Option value={2}>Finance dept.</Select.Option>
<Select.Option value={3}>Research and development department</Select.Option>
</Select>
</Form.Item>
</Form>
</div>
</Modal>
</div>
);
}
Copy the code
Any questions? No problem.
But now there’s a requirement to open the new user popover in 10 places. How to do? Copy bai!
Is there a problem with that? No problem.
Here comes another requirement: you also need a new department popover. How to do? Continue copy bai!
Change the title of the popover, change the fields and submit of the form.
Does so much copy matter? It doesn’t matter. Because it’s quick.
But now comes a requirement: add a field to the new user form
So here’s the problem! How do I know which files MY copy code is scattered in? Will it be corrected?
At this time think of, we have a killer mace, called reuse
So the question is, how do you reuse it?
Component multiplexing Trilogy
Component reuse looks simple, but many people are either easy to use or painful to use; Or painful to do and complicated to use.
So if you reuse this new user popover, the first question is what are you going to reuse?
Clarify objects and decide what to reuse
We have three objects: new user Page Page, Button popup, Modal popup, form From, these three objects have some functions respectively:
- Page: provides popover open/close function (open/close), provides method to trigger form submission (submit), provides form object (form.useform ()), receives form output and submits to server
- Button: Open popover (call open)
- Modal: Provides click cancel and confirm events (onCancel/onOk)
- Form: Form validation, which provides an onFinish event to output Form values
The most complex and variable thing here is the popover and its contents, so the popover and its contents (forms) need to be reused the most.
So what’s holding us back?
The Page needs a Form object to submit the form, and you need to turn Off Modal after the form submission. Who do these actions belong to?
To determine the boundary
The simplest object here is Button, which is a fool and only has an onClick action that depends on the page.
Modal is also not complicated, it provides cancellation and confirmation callbacks, and the content is also provided externally. So when we reuse a Modal, we use its style configuration
Form: We reuse a Form and naturally want to reuse the submission logic of the Form. Therefore, in addition to form validation, it should also assume the responsibility of the request server.
Into components
Encapsulate useModal, remove Modal:
useModal.tsx:
import { Modal } from 'antd';
import type { ModalProps } from 'antd';
import * as React from 'react';
import type { MutableRefObject } from 'react';
interface PropsType<T> extends Omit<ModalProps, 'onOk'> {
onOk: (ref: MutableRefObject<T | undefined>) = > void;
}
function withModal<T = any> (modalProps? : ModalProps, slotProps? :any) {
return function (Slot: React.FC<any>) {
return (props? : PropsType
) = > {
const ref = React.useRef<T>();
return (
<div>
<Modal
wrapClassName="modal-wrap"
okText="Submit"
cancelButtonProps={{ shape: 'round'}}okButtonProps={{ shape: 'round'}}width={600}
{. modalProps}
{. props}
onOk={()= >props? .onOk? .(ref)} ><Slot {. slotProps} ref={ref} close={props? .onCancel} />
</Modal>
</div>
);
};
};
}
export default withModal;
Copy the code
Detach the form and expose the REF
UserForm.tsx:
import { Form, Input, Select } from 'antd';
import type { FormInstance } from 'antd/es/form';
import React from 'react';
typePropsType = React.PropsWithChildren<{ afterSubmit? :(values: any, form: FormInstance<any>) = > void; } >.const UserForm = (props: PropsType, ref? : React.ForwardedRef
) = > {
const [form] = Form.useForm();
// Get the form data after submission, request the interface, reset the form and close
const onSubmit = (values: any) = > {
console.log(values);
//await fetch ...form.resetFields(); props.afterSubmit? .(values, form); };return (
<div className="form">
<Form
onFinish={onSubmit}
ref={ref}
form={form}
labelCol={{ span: 4 }}
wrapperCol={{ span: 16 }}
>
<Form.Item
label="Username"
name="username"
rules={[{ required: true.message: 'Please input username!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="User email"
name="mail"
rules={[{ required: true.message: 'Please input mail!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="Department"
name="depart"
rules={[{ required: true.message: 'Please input depart!' }]}
>
<Select>
<Select.Option value={1}>The Marketing Department</Select.Option>
<Select.Option value={2}>Finance dept.</Select.Option>
<Select.Option value={3}>Research and development department</Select.Option>
</Select>
</Form.Item>
</Form>
</div>
);
};
export default UserForm;
Copy the code
forwardRef
For this particular scenario, the submission of the form is controlled by useModal, so you need to expose the form or ref; It is also possible to provide a Submit button by the form itself and hide the modal footer, but this is not cost-effective when there are many forms.
Using useModal:
Const UserFormModal = withModal({popover props}, {popover content component (form) props})(react.forwardref (UserForm));Copy the code
In the end, the page has half the code missing
export default function TestPage() { const [visiable, setVisiable] = useState(false); // Unavailable const open = () => {setVisiable(true); }; // Unavailable const close = () => {setVisiable(false); }; Const submit = (ref: MutableRefObject<FormInstance>) => {ref.current.submit(); }; const afterSubmit = () => { close(); }; Const UserFormModal = withModal({title: 'New user'}, {afterSubmit})(reace.forwardref (UserForm)); Return (<div> <div className="text-center"> <Button type="primary" onClick={open}> New </Button> </div> <UserFormModal visible={visiable} onCancel={close} onOk={submit} /> </div> ); }Copy the code
Embrace the hooks
This is typical HOC thinking, but if we use hooks, this code could be reduced by 50%. Next, how to package useModal