1. Question raising

Suppose our project is built with Element-UI

1. How do we make our ElDialog dragable without changing a line of code?

2. How to make our Selector achieve hanyu Pinyin fuzzy matching under the premise of modifying a line of code?

3. How to make our Tree expand or shrink nodes only when the arrow icon is clicked, and the Tree node content can display small ICONS (native support of AntD UI) under the premise of modifying a line of code?

With these problems in mind, we can easily imagine that we need to encapsulate these components. If we encapsulate these components, our design should consider how to improve the usability of components. My conclusion is as follows: Instead of changing the API of the component itself, we introduce the concept of higher-order components by adding extended apis that meet the specific functions described above.

2. What are higher-order components

HOC is a concept derived from React’s official documentation

HOC is an advanced technique used in React to reuse component logic. HOC itself is not part of the React API; it is a design pattern based on the composite features of React.Copy the code

Specifically, a higher-order component is a function that takes a component and returns a new component. Therefore, as far as I personally understand, the essence of high-order components is an embodiment of AOP programming ideas in the front-end framework, the so-called under the premise of not changing the function of the component itself or ease of use, extend some new functions, so as to achieve better component packaging, code reuse a means.

3. How to write higher-order components in Vue components

First, let’s look at some of the required apis

3.1 $attrs

Here’s an excerpt from Vue’s official documentation:

Contains attribute bindings (except class and style) that are not recognized (and retrieved) as prop in the parent scope. When a component does not declare any prop, all parent-scoped bindings (except class and style) are included, and internal components can be passed in via V-bind ="$attrs" -- useful when creating higher-level components.Copy the code

Attribute bindings (except class and style) that are not recognized (and retrieved) as prop in parent scope

3.2 $listeners

This is an excerpt from our official Vue documentation

Contains v-ON event listeners in the parent scope (without the.native modifier). Internal components can be passed in via V-on ="$Listeners "-- useful for creating higher-level components.Copy the code

Note: The v-ON event listener in the parent scope (without the.native modifier) can be used together to achieve magical effects. We are now ready to try to write a component to ElDialog that implements drag. The dialog. vue component code is as follows:

<template> <el-dialog v-drag="draggable" v-bind="$attrs" v-on="$listeners"></el-dialog> </template> <script> // drag Import drag from '@/utils/drag'; import drag from '@/utils/drag'; export default { name: 'Dialog', directives: { drag }, props: { draggable: { type: Boolean, required: false, default: true } } } </script>Copy the code

As a result of this coding, we can pass external props or event listeners through this level of code without affecting the functionality of the ElDialog itself, but at this point the draggable functionality we’ve given us is implemented. 🤣 😎 🥰

4, add

Although $listeners and $attrs have implemented our property and event listener penetration, there are currently two issues we haven’t considered. One is the component’s slot, which the code above has not yet penetrated, and the other is the component’s refs fetch. The first problem is easily solved by adding support for slots to the code above. The rewritten dialog.vue code looks like this:

<template> <el-dialog v-drag="draggable" v-bind="$attrs" v-on="$listeners"> <slot name='header' /> <slot /> <slot Name ='footer' /> </el-dialog> </template> <script> // drag =' drag'; export default { name: 'Dialog', directives: { drag }, props: { draggable: { type: Boolean, required: false, default: true } } } </script>Copy the code

The second problem is particularly tricky 😫😫😫, and the scenario is as follows:

<template> <! <drag-dialog ref=' Dialog '/> </template>Copy the code

Suppose our ElDialog has methods (because of my example, the official ElDialog API does not reserve available methods), then our ref is fetching our DragDialog, when in fact we want to fetch the real ElDialog. This is the refs forwarding problem. As far as Vue2. X is concerned, the author has not found a simple API that can implement reference forwarding, so we need to write additional code to support it. The code is as follows :(if there are other ways, please contact my email, [email protected]), I hope the handsome boss you can consider the appeal of my younger brother, heh heh 🤣🤣🤣

<template> <el-dialog v-drag="draggable" v-bind="$attrs" v-on="$listeners"> <slot name='header' /> <slot /> <slot Name ='footer' /> </el-dialog> </template> <script> // drag =' drag'; import { ref } from '@vue/composition-api'; export default { name: 'Dialog', setup() { const dialog =ref(null); return { dialog }; }, directives: { drag }, props: { draggable: { type: Boolean, required: false, default: true } }, methods: {sayHello() {// Suppose sayHello is a useful method reserved for ElDialog. this.dialog && this.dialog.sayHello(); } } } </script>Copy the code

React supports forwarding by reference. React supports forwarding by reference.

Ref forwarding is a technique for automatically passing a Ref through a component to one of its children. This is usually not required for components in most applications. But it can be useful for some components, especially reusable component libraries.Copy the code

Take its official 🌰 :

const FancyButton = React.forwardRef((props, ref) = > (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));

// You can get the DOM button ref directly:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
Copy the code

5. Conclusion: Advantages and disadvantages of higher-order components

5.1 Advantages of higher-order Components:

Reduce the redundancy of props or V-ON/EMIT code.

2, it can realize the function of increasing the UI library itself, without affecting the call of components, which is conducive to the collaboration of the team, and also conducive to the later personnel for project maintenance.

3, can carry out unified management in the project, reduce coding and management costs, reduce the generation of unnecessary bugs.

5.2 Disadvantages of higher-order Components:

1. There are certain requirements for the quality of developers. If business code is carelessly mixed into such components, it will be a potential trouble for the later development.

2. As higher-order components cover a layer compared with the original components, their performance will be lower than that of the original components, so we need to use higher-order components reasonably in the project.

Due to the limited level of the author, it is inevitable to make mistakes in the writing process. If there are any mistakes, please correct them. Your opinions will help me make better progress. Please contact the author at [email protected]🥰 if you want to reprint this article