Vue3: Optimization of multi-Modal box business logic code
background
The following scenarios are often encountered in development. Click the button to control the display and hide of the pop-up box through variable Visible:
export default defineComponent({
setup() {
const visible = ref(false);
return {
visible,
};
},
render() {
return (
<>
<Button
onClick={()= >{ this.visible = true; }} > Displays the pop-up box</Button>
<MyModal
visible={visible}
onClose={()= >{ this.visible = false; }} / ></>); }});Copy the code
However, as the business continues to iterate, there may be multiple Modal boxes on a page. At this time, how should we deal with them more elegantly? Such as:
export default defineComponent({
setup() {
const visible1 = ref(false);
const visible2 = ref(false);
const visible3 = ref(false);
return {
visible1,
visible2,
visible3,
};
},
render() {
return (
<>
<Button
onClick={()= >{ this.visible1 = true; }} > Display dialog box 1</Button>
<Button
onClick={()= >{ this.visible2 = true; }} > Display dialog box 2</Button>
<Button
onClick={()= >{ this.visible2 = true; }} > Display dialog box 3</Button>
<MyModal1
visible={visible1}
onClose={()= >{ this.visible1 = false; }} / ><MyModal2
visible={visible2}
onClose={()= >{ this.visible2 = false; }} / ><MyModal3
visible={visible3}
onClose={()= >{ this.visible3 = false; }} / ></>); }});Copy the code
In the case of multiple Modal boxes, this method obviously doesn’t look elegant. Verbose code is not concise enough.
We usually should be so development, the question is whether there is a better way?
Train of thought
This is certainly the case with the declarative component development pattern. But if we can call the component functionally, it might be a good choice. The pseudocode is as follows:
export default defineComponent({
render() {
return (
<>
<Button
onClick={()= >{ MyModal1({}).then((data) => {}); }} > Display dialog box 1</Button>
<Button
onClick={()= >{ MyModal2({}).then((data) => {}); }} > Display dialog box 2</Button>
<Button
onClick={()= >{ MyModal3({}).then((data) => {}); }} > Display dialog box 3</Button>
</>); }});Copy the code
This way, at least, is more logical and readable. This is the way I recommend and like it. I used react to encapsulate basic components. Now switch to the VUE3 technology stack and try it again.
How to implement
First paste the code, the basic package component implementation (logic see code comments):
import { createVNode, render, VNodeTypes } from "vue";
export async function DynamicModal(component: VNodeTypes, props: object) {
return new Promise((resolve) = > {
1️ ⃣// Create a parent package object
const container = document.createElement("div");
2️ ⃣// Create vue Node instance and pass props(force pass onClose method)
constvm = createVNode(component, { ... props,onClose: (params: unknown) = > {
5️ ⃣// The onClose method is called when the child component is destroyed to remove the responding DOM node
document.body.removeChild(
document.querySelector(".ant-modal-root")? .parentNodeas Node
);
document.body.removeChild(container);
6️ ⃣// Returns the data passed by the child componentresolve(params); }});3️ ⃣// Render the vue Node instance into the wrap object
render(vm, container);
4️ ⃣// Insert the wrap object into the body
document.body.appendChild(container);
});
}
Copy the code
How to use:
// CellModal.tsx
// Define the component
const CellModal = defineComponent({
props: {
onClose: {
type: Function as PropType<(param: unknown) = > void>,
required: true,},props1: {
type: String as PropType<string>,
required: true,},props2: {
type: Object as PropType<any>,
required: true,}},setup(props) {
const visible = ref(true);
const close = () = > {
visible.value = false;
props.onClose({ text: "This is the child component's data." });
};
return () = > (
<Modal
title="Title"
visible={visible.value}
onCancel={close}
onOk={()= >{message.success(" operation succeeded "); close(); }} > // Contents</Modal>); }});// Export wrapped components
export async function DynamicCellModal(props) {
return DynamicModal(CellModal, props);
}
// Edit.tsx
// Use the component
import { DynamicCellModal } from "./CellModal";
DynamicCellModal({ props1: "".props2: {} }).then((data) = > {
// data = {text: 'This is the child component's data'}
});
Copy the code
At this point someone noticed that when DynamicCellModal was called, props did not type check. Okay, arrangement
In this case, CellModal props contains three properties: onClose, props1, props2; Since onClose is dynamically forced by us, there is no need for the caller to pass it in. All props other than onClose need to be prompted
We need to get the props type of the component CellModal and then exclude onClose
- First declare the type:
1️ ⃣// Get the props type of the component
type GetPropType<T extends new(... args:any) = >void> = {
[P in keyof InstanceType<T>["$props"]]: InstanceType<T>["$props"][P];
};
2️ ⃣// Delete the onClose type
type DynamicPropType<T extends new(... args:any) = >void> = Omit<
GetPropType<T>,
"onClose"
>;
Copy the code
- Use type (modify cellmodal.tsx file)
// CellModal.tsx
export async function DynamicCellModal(
props: DynamicPropType<typeof CellModal>
) {
return DynamicModal(CellModal, props);
}
Copy the code
At this point, the whole transformation is complete.
Add a scene
export const SuccessModal = defineComponent({
setup() {
2️ ⃣// This scenario needs to define two variables
const success = ref({ msg: ' ' })
const visible = ref(false)
return () = > (
<div>
<Button
onClick={()= >{1️ // Submit form, interface returns data, display modal display content submit(). Then ((res: {MSG: string }) => { visible.value = true success.value.msg = res.msg }) }} ><div class='text'>submit</div>
</Button>
<Modal show={visible.value}>
<div>{success.value.msg}</div>
</Modal>
</div>)}})Copy the code
With our optimization, the code could look like this:
export const DynamicSuccessModal = defineComponent({
setup() {
return () = > (
<Button
onClick={()= > {
submit().then((res: { msg: string }) => {
DynamicModal({ msg: res.msg });
});
}}
>
<div class="text">submit</div>
</Button>); }});Copy the code
We have any good way, welcome to discuss!