HOC in vue 3 (higher order components)
Tags: vue, javascript
Published: 2021-05-26 11:56:31

lets talk about an advanced technique for reusing components, its a function will takes a component and give back new component. the idea comes from react community, [here's the link](https://reactjs.org/docs/higher-order-components.html) . this idea maybe not so useful for every project but for third party libraries its great pattern.

## init project
ok we will create project with vite because its fast. [more details about vite](https://vitejs.dev/guide). use this commands to create project
```sh
npm init @vitejs/app myapp
cd myapp
npm install
npm run dev

// or yarn 
yarn create @vitejs/app myapp
cd myapp
yarn
yarn dev
```
we will create two components for counter ` CounterPlusOne.vue ` and ` CounterPlusFive.vue ` as the names will tell first component plus by one the counter, the second component plus counter by five. the components will be simple and look like this:
```vue
<!-- CounterPlusOne.vue -->
<template>
  <p>counter {{ counter }}</p>
  <button @click="increment">add</button>
</template>

<script>
export default {
  data: () => ({
    counter: 0,
  }),

  methods: {
    increment() {
      this.counter += 1;
    },
  },
};
</script>

<!-- CounterPlusFive.vue -->
<template>
  <p>counter {{ counter }}</p>
  <button @click="increment">add</button>
</template>

<script>
export default {
  data: () => ({
    counter: 0,
  }),

  methods: {
    increment() {
      this.counter += 5;
    },
  },
};
</script>

```
as you can see things are similar in script part. the state, and maybe increment part if we could pass a prop to it. so we will extract this functionality to a function but gives us back the component we want.

## HOC (higher order components)
the existing part is this function, but you have to know about render function and how it works in vue 3, [the doc link](https://v3.vuejs.org/guide/render-function.html) . the summary of render function, its a vue function that create elements. and vue use it behind the scenes. but we dont have to know about it because we can use template in vue components. lets create a javascript file ` WithCounter.js ` and for now we just want to pass the component and give it back to us, simple and ez :)
```js
import { h } from "@vue/runtime-core"

function WithCounter(WrappedComponent) {
  // we will return new component that render's WrappedComponent
  return {
    created() {
      console.log('HOC component created')
    },

    render() {
      return h(WrappedComponent)
    }
  }
}

export default WithCounter
```

this is our first higher order component, maybe lots of things is new but i will explain. so first we have a function that accepts a component to render, remember the HOC component dont care about component it will render, it just make it resuable. our function  will returns an object, this object is a new component, the components in vue is just objects in vue components you will export default an object in script part. render remains it will create a vNode with ` h ` function we imported at top. if we want to create a custom component with render function thats how we do it. to use this higher order component:
```js
import CounterPlusOne from "./components/CounterPlusOne.vue";
import CounterPlusFive from "./components/CounterPlusFive.vue";
import WithCounter from "./components/WithCounter.js";

export default {
  components: {
    CounterPlusOne: WithCounter(CounterPlusOne),
    CounterPlusFive: WithCounter(CounterPlusFive),
  },
};
```
for now it doesn't do much just renders the components and log ` HOC component created ` twice.

## reusable HOCs
now we move counter functionality to our HOC. first start simple we just send a argument to with counter, means:
```js
// App.vue
export default {
  components: {
    CounterPlusOne: WithCounter(CounterPlusOne, 1),
    CounterPlusFive: WithCounter(CounterPlusFive, 5),
  },
};

// WithCounter.js
function WithCounter(WrappedComponent, number)
```
because its function we can pass as many arguments we want. lets move all duplicated code in this two components to WithCounter:
```js

function WithCounter(WrappedComponent, number = 1) {
  return {
    data: () => ({
      counter: 0,
    }),

    methods: {
      increment() {
        this.counter += number;
      },
    },

    render() {
      return h(WrappedComponent, {
        counter: this.counter,
        increment: this.increment
      })
    }
  }
}
```
as i said increment method can be refactored with given parameter. we only do pass counter and increment as props to components. so our component will be like this.
```vue
<!-- CounterPlusOne.vue -->
<template>
  <p>counter {{ counter }}</p>
  <button @click="increment">add</button>
</template>

<script>
export default {
  props: {
    counter: Number,
    increment: Function,
  },
};
</script>

<!-- CounterPlusFive.vue -->
<template>
  <p>counter {{ counter }}</p>
  <button @click="increment">add</button>
</template>

<script>
export default {
  props: {
    counter: Number,
    increment: Function,
  },
};
</script>
```

## summary
for now they totally same but its just an example, i use this pattern in my ` tdata ` package, that just sends form and get data from an api. i dont use this pattern a lot but i see its very useful for packages or third party libraries.