HOC is an architectural pattern that is common in React, but can also be used in Vue. It can be described as a way to share common functionality between components without repeating code. HOC is designed to enhance the functionality of components. It allows for reusability and maintainability in projects.
As long as you pass in a component to a method and then return a new component, it’s a HOC.
Higher-order components are useful for:
- Operation properties.
- Manipulating data and data abstraction.
- Code reuse
Preliminary knowledge
Before we start the tutorial, here are some things to know:
- Experience with the Vue framework.
- Know how to set up applications using VUe-CLI.
- Basic knowledge of JavaScript and Vue
- Node (8)
- NPM (5.2.0)
Before starting this tutorial, make sure you have Node and NPM installed.
Higher-order component patterns in Vue
Although higher-order components are usually associated with React, it is possible to create higher-order components for Vue components. The pattern for creating higher-order components in Vue is shown below.
// hocomponent.js
import Vue from 'vue'
import ComponentExample from '@/components/ComponentExample.vue'
const HoComponent = (component) => {
return Vue.component('withSubscription', {
render(createElement) {
return createElement(component)
}
}
}
const HoComponentEnhanced = HoComponent(ComponentExample);
Copy the code
As shown in the code block above, the HoComponent function takes a component as an argument and creates a new component to render the component passed in.
A simple HOC example
In this tutorial, we’ll look at an example that uses higher-order components. Before introducing higher-order components, we’ll look at how the current code base works without higher-order components, and then see how to abstract. Codesandbox. IO/embed/llvq0…
As shown in the CodeSandbox above, the application displays a list of paper companies and their net worth, as well as people from The Office (US) and their awards.
The mockData.js file is the only source of all the data we need for our application.
// src/components/mockData.js
const staff = [
{
name: "Michael Scott",
id: 0,
awards: 2
},
{
name: "Toby Flenderson",
id: 1,
awards: 0
},
{
name: "Dwight K. Schrute",
id: 2,
awards: 10
},
{
name: "Jim Halpert",
id: 3,
awards: 1
},
{
name: "Andy Bernard",
id: 4,
awards: 0
},
{
name: "Phyllis Vance",
id: 5,
awards: 0
},
{
name: "Stanley Hudson",
id: 6,
awards: 0
}
];
const paperCompanies = [
{
id: 0,
name: "Staples",
net: 10000000
},
{
id: 1,
name: "Dundler Mufflin",
net: 5000000
},
{
id: 2,
name: "Michael Scott Paper Company",
net: 300000
},
{
id: 3,
name: "Prince Family Paper",
net: 30000
}
];
export default {
getStaff() {
return staff;
},
getCompanies() {
return paperCompanies;
},
increaseAward(id) {
staff[id].awards++;
},
decreaseAward(id) {
staff[id].awards--;
},
setNetWorth(id) { paperCompanies[id].net = Math.random() * (5000000 - 50000) + 50000; }};Copy the code
In the code snippet above, there are several const variables that hold the list of companies and employees. We have also exported some functions that do the following:
- Return to employee list
- Back to list of companies
- Increase and decrease rewards for specific employees, and finally
- Finally, set the company’s net worth
Next, let’s look at the staff. vue and Companies. Vue components.
// src/components/Staff.vue
<template>
<main>
<h3>Staff List</h3>
<div v-for="(staff, i) in staffList" :key="i"> {{staff. Name}}: {{staff. Awards}} 🎉 < button@click ="increaseAwards(staff.id);">+</button>
<button @click="decreaseAwards(staff.id);">-</button>
</div>
</main>
</template>
<script>
import mockData from "./mockData.js";
export default {
data() {
return{ staffList: mockData.getStaff() }; }, methods: { increaseAwards(id) { mockData.increaseAward(id); this.staffList = mockData.getStaff(); }, decreaseAwards(id) { mockData.decreaseAward(id); this.staffList = mockData.getStaff(); }}}; </script>Copy the code
In the above code block, the data instance variable staffList is assigned to the content returned by mockData.getstaff (). We also have the increaseAwards and decreaseAwards functions, which call mockData.increaseaward and mockData.decreaseAward, respectively. The ids passed to these functions are obtained from the rendered template.
// src/components/Companies.vue
<template>
<main>
<h3>Paper Companies</h3>
<div v-for="(companies, i) in companies" :key="i">
{{ companies.name }} - ${{ companies.net
}}<button @click="setWorth(companies.id);">Set Company Value</button>
</div>
</main>
</template>
<script>
import mockData from "./mockData.js";
export default {
data() {
return {
companies: mockData.getCompanies()
};
},
methods: {
setWorth(id) { mockData.setNetWorth(id); this.companies = mockData.getCompanies(); }}}; </script>Copy the code
In the code block above, the data instance variable companies is assigned to the return content of mockData.getCompanies(). We also have the setWorth function, which sets a random value as a net value by passing the company ID to MockData.setnetworth. The ID passed to the function is obtained from the rendered template.
Now that we have seen how the two components work, we can identify what they have in common and abstract it as follows:
- Get data from a data source (mockData.js)
- The onClick function
Let’s look at how you can put the above operations into higher-order components to avoid code duplication and ensure reusability.
You can continue to create a new Vue project using vue-CLI. Vue CLI is a complete system for rapid development of vue.js projects, ensuring that you have a usable development environment without having to build configurations. You can install vuE-CLI using the following command.
npm install -g @vue/cli
Copy the code
Once installed, you can create a new project with vue-hoComponent as the name of the application. Be sure to select default default, as no custom options are required.
vue create vue-hocomponent
Copy the code
Once installed, you can go ahead and create the following files and then edit them using the snippet content shared above.
src/components
In the directoryStaff.vue
File.src/components
In the directoryCompanies.vue
File.src/components
In the directorymockData.js
File.
Alternatively, you can fork the app in CodeSandbox and follow this tutorial.
The next step is to create a high-level component file for abstraction. Create a file named hoComponent.js in the SRC folder and edit the following:
// src/components/HoComponent.js
import Vue from "vue";
import mockData from "./mockData.js";
const HoComponent = (component, fetchData) => {
return Vue.component("HoComponent", {
render(createElement, context) {
return createElement(component, {
props: {
returnedData: this.returnedData
}
});
},
data() {
return{ returnedData: fetchData(mockData) }; }}); };export default HoComponent;
Copy the code
In the code above, data from the Vue and mockData files is imported into the component.
The HoComponent function takes two arguments, a component and fetchData. The fetchData method is used to determine what to display in the presentation component. This means that wherever higher-order components are used, functions passed as fetchData will be used to get data from mockData.
The data instance returnedData is then set to the content of fetchData, which is then passed as props to the new component created in the higher-order component.
Let’s take a look at how the newly created high-level components are used in the application. We need to edit Staff. Vue and Companies. Vue.
// src/components/Staff.vue
<template>
<main>
<h3>Staff List</h3>
<div v-for="(staff, i) in returnedData" :key="i"> {{staff. Name}}: {{staff. Awards}} 🎉 < button@click ="increaseAwards(staff.id);">+</button>
<button @click="decreaseAwards(staff.id);">-</button>
</div>
</main>
</template>
<script>
export default {
props: ["returnedData"]}; </script>Copy the code
// src/components/Companies.vue
<template>
<main>
<h3>Paper Companies</h3>
<div v-for="(companies, i) in returnedData" :key="i">
{{ companies.name }} - ${{ companies.net
}}<button @click="setWorth(companies.id);">Set Company Value</button>
</div>
</main>
</template>
<script>
export default {
props: ["returnedData"]}; </script>Copy the code
As you can see in the code block above, for both components, we removed the function and data instance variables, and all the data needed to display the content will now be obtained from these props. And we’ll talk about the deleted functions in a second.
In the app. vue component, edit the existing content in the script tag with the following code:
// src/App.vue
<script>
// import the Companies component
import Companies from "./components/Companies";
// import the Staff component
import Staff from "./components/Staff";
// import the higher order component
import HoComponent from "./components/HoComponent.js";
// Create a const variable which contains the Companies component wrapped in the higher order component
const CompaniesComponent = HoComponent(Companies, mockData => mockData.getCompanies()
);
// Create a const variable which contains the Staff component wrapped in the higher order component
const StaffComponent = HoComponent(Staff, mockData => mockData.getStaff());
export default {
name: "App",
components: {
CompaniesComponent,
StaffComponent
}
};
</script>
Copy the code
In the code block above, the HoComponent wraps the Staff and Companies components. Each component is passed in as the first argument to the HoComponent, and the second argument is a function that returns another function specifying what data should be fetched from mockData. This is the fetchData function from the higher-order component (hoComponent.js) we created earlier.
If you refresh the application now, you should still see the data from the mockData file rendered as usual. The only difference is that these buttons don’t work because they haven’t been bound to any functions yet. Let’s solve this problem.
Let’s first modify the two files staff. vue and Companies. Vue:
// src/components/Staff.vue
<template>
<main>
<h3>Staff List</h3>
<div v-for="(staff, i) in returnedData" :key="i"> {{staff. Name}}: {{staff. Awards}} 🎉 < button@click ="$emit('click', { name: 'increaseAward', id: staff.id });">
+
</button>
<button @click="$emit('click', { name: 'decreaseAward', id: staff.id });">
-
</button>
</div>
</main>
</template>
<script>
export default {
props: ["returnedData"]}; </script>Copy the code
// src/components/Companies.vue
<template>
<main>
<h3>Paper Companies</h3>
<div v-for="(companies, i) in returnedData" :key="i">
{{ companies.name }} - ${{ companies.net
}}<button
@click="$emit('click', { name: 'setNetWorth', id: companies.id });"
>
Set Company Value
</button>
</div>
</main>
</template>
<script>
export default {
props: ["returnedData"]}; </script>Copy the code
In the two code snippets above, we send events that will be used in the parent component app.vue. We send an object that contains the value, the name of the function associated with the action being attempted, and the ID of the element being clicked. Don’t forget that the mockData.js file defines the increaseAward, decreaseAward, and setNetWorth functions.
Next, we start editing the parent component app.vue to respond to the data sent by the child component. App.vue changes as follows:
// src/App.vue
<template>
<div id="app">
<CompaniesComponent @click="onEventHappen" />
<StaffComponent @click="onEventHappen" />
</div>
</template>
<script>
// import the Companies component
import Companies from "./components/Companies";
// import the Staff component
import Staff from "./components/Staff";
// import the higher order component
import HoComponent from "./components/HoComponent.js";
// import the source data from mockData only to be used for event handlers
import sourceData from "./components/mockData.js";
// Create a const variable which contains the Companies component wrapped in the higher order component
const CompaniesComponent = HoComponent(Companies, mockData =>
mockData.getCompanies()
);
// Create a const variable which contains the Staff component wrapped in the higher order component
const StaffComponent = HoComponent(Staff, mockData => mockData.getStaff());
export default {
name: "App",
components: {
CompaniesComponent,
StaffComponent
},
methods: {
onEventHappen(value) {
// set the variable setFunction to the name of the function that was passed iin the value emitted from child component i.e. if value.name is 'increaseAward'.setFunction is set to increaseAward()
let setFunction = sourceData[value.name];
// call the corresponding function with the id passed as an argument.
setFunction(value.id); }}}; </script> <style>#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Copy the code
In the code block above, we added an event listener to the app.vue component. Vue components. The onEventHappen method will be invoked whenever the staff. vue or Companies. Vue component is clicked.
In the onEventHappen method, we set the variable setFunction to the name of the function passed from the value emitted by the child component, that is, if value.name is ‘increaseAward’, setFunction is set to increaseAward(). SetFunction will be executed with id as an argument.
Finally, to pass event listeners to components wrapped in higher-order components, we need to add the following line of code to the hoComponent.js file.
props: { returnedData: this.returnedData }, on: { ... this.$listeners }
Copy the code
You can now refresh the app and all the buttons work properly.
vue-hoc
Alternatively, you can use vue-Hoc libraries to help create higher-order components. Vue-hoc helps you easily create higher-order components by passing basic components, a set of component options that are applied to Hoc, and data properties that are passed to the component during the rendering phase.
Vue-hoc can be installed with the following command:
npm install --save vue-hoc
Copy the code
Vue-hoc has some examples of plug-ins that can get you started creating higher-level components, which you can check out here.
conclusion
As we learned in this tutorial, the primary use of higher-order components is to enhance the reusability and logic of presentation-class components in your application. It was also learned that higher-order components are useful for:
- Operation properties.
- Manipulating data and data abstraction.
- Code reuse
Then we looked at an example of how to create and use higher-order components in Vue. The source code for the sample is available on GitHub.
communication
Welcome to follow the wechat public account “1024 Translation station”