Trying the composition API in Vue3
Tags: vue, vue3
Published: 2019-10-09 03:44:00

---
title: Trying the composition API in Vue3
published: true
description: My first attempt at trying the composition API.
tags: vue, vue3
---

# Trying the composition API in Vue3

On October 4th, The Vue-3 code was made public at [github.com/vuejs/vue-next](https://github.com/vuejs/vue-next)
It is still in pre-alpha monorepo that you can't quite use the same way as Vue2 just yet. There is also functionality from Vue2 that hasn't reached parity **yet**. At the time of writing, these are SSR, kep-alive, transitions, DOM-specific transforms (v-on, v-dom, etc.)

It does allow us, however, to start playing with though.

I spent the last couple nights trying to make some sample code to work. Unfortunately, the going was a bit tough. I wanted to start by making a parent component pass a prop to a child component. It took me a lot longer than I expected, but that was mostly my fault. I'll try to describe the path I took to get it eventually to work, and some of the mistakes I've made.

## Get the repo

I started by downloading the git repo with the following commands

```sh
# clone the vue-next mono repo to vue-next folder
git clone https://github.com/vuejs/vue-next.git

cd vue-next

# install dependencies
npm install

# build vue.global.js
npm run dev
```

this will generate a `vue.global.js` file at `packages\vue\dist\vue.global.js`

Of course later I realized that the best place to start is here: https://github.com/vuejs/vue-next/blob/master/.github/contributing.md

The problem I had was that the file that was generated mounts `Vue` as a global, so is not suited for use with bundlers like `parcel` or `webpack`, which as what I was trying to use it with. In the contributing link from the repo, there are further instructions for generating other builds, but I've decided to use the global package instead with a simple file server (like `serve` or `http-sever`), it even works on online code editors like jsfiddle, which I ended up using.

I've found a sample code from the vue-composition-api-rfc at https://vue-composition-api-rfc.netlify.com/#basic-example

```html
<template>
  <button @click="increment">
    Count is: {{ state.count }}, double is: {{ state.double }}
  </button>
</template>

<script>
  import { reactive, computed } from "vue";

  export default {
    setup() {
      const state = reactive({
        count: 0,
        double: computed(() => state.count * 2)
      });

      function increment() {
        state.count++;
      }

      return {
        state,
        increment
      };
    }
  };
</script>
```

First I made it available by uploading it to gitlab as a gist and generating a rawgist link for includeing in a jsfiddle

https://gistcdn.githack.com/dasDaniel/f3cebc1274339055729c6e887ca8d2ad/raw/8f0432bfd812c453cdecee61862963fe3e24119a/vue.global.js

I had to make some changes to make it work with the global package, since that doesn't support Single File Components.

HTML:

```html
<div id="app"></div>

<template id="appTemplate">
  <button @click="increment">
    Count is: {{ state.count }}, double is: {{ state.double }}
  </button>
</template>
```

JS:

```js
const { reactive, computed } = Vue

const MyComponent = {
  setup(props) {
    const state = reactive({
      count: 0,
      double: computed(() => state.count * 2)
    });

    function increment() {
      state.count++
    }

    return {
      state,
      increment
    };
  },
  template: document.getElementById("appTemplate").innerHTML
};

const app = Vue.createApp({})
app.mount(MyComponent, "#app")
```

As you can see, instead of using template literals or SFC, I used the `<template>` html tag and referenced it with `getElementById`. Otherwise it's pretty much the same.

The next goal was to add another component and pass a prop to it.

I added the following code to the `appTemplate`
```html
<my-component :count="state.count"></my-component>
```

and this to the script
```js

const MyComponent = Vue.createComponent({
  template: `<div>my component {{count}}<div>`,
  props: {
  	count: Number
  },
  setup(props){
    return {...props}
  }
})
```

I also registered the component before mounting the app
```js
app.component('my-component', MyComponent)
```

The result was that the prop was passed initially with a value of `0`, and then it didn't update any after that. So I tried copying what the app does.

```js
const MyComponent = Vue.createComponent({
  template: `<div>my component {{state.count}}<div>`,
  props: {
  	count: Number
  },
  setup(props){
    const state = reactive({...props})
    return {
    	state
    }
  }
});
```

Shis still doesn't work and it's not clear to me why.

Now is the time when I frantically try a hundred different things and nothing seems to work. I could list all the things, but I'll just mention a few.

```js
// added `onUpdated`
const { reactive, computed, onUpdated } = Vue;

const MyComponent = Vue.createComponent({
  template: `<div>my component {{state.count}}<div>`,
  props: {
  	count: Number
  },
  setup(props){
    const state = reactive({...props})
    // added onUpdated function
    onUpdated(() => {
    	state.count = props.count
    })
    return {
    	state
    }
  }
});
```

When I console logged the state after the update, the state did change, but the template did not update. This wasn't making any sense. 

Eventually after more reading and debugging I've found out two things.

The way to do it correctly from what I gather is to use `reactive` AND `computed`

```js
  const state = reactive({
  	count: computed(() => props.count)
  })
```

The other thing that I eventually noticed, was that my `div` tag wasn't closed. This was causing the layout to only render the first time, and likely why I may have tried something that should have been working (like using `onUpdate`) and weren't.

the working code (https://jsfiddle.net/dapo/fp34wj6z/)

```html
<div id="app"></div>

<template id="appTemplate">
  <button @click="increment">
    Count is: {{ state.count }}, double is: {{ state.double }}
  </button>
  <pre>{{ state }}</pre>
  <my-component :count="state.count"></my-component>
</template>

<template id="myComponent">
  <div>
    Child
    <pre>{{ state }}</pre>
  </div>
</template>
```

```js
const {
  reactive,
  computed,
} = Vue;

const MyComponent = Vue.createComponent({
  props: {
    count: Number
  },
  setup(props) {
    const state = reactive({
      count: computed(() => props.count),
      triple: computed(() => props.count * 3)
    });

    return {
      state,
    }
  },
  template: document.getElementById('myComponent').innerHTML
});

const App = {
  setup(props) {
    const state = reactive({
      count: 0,
      double: computed(() => state.count * 2)
    });

    function increment() {
      state.count++;
    }

    return {
      state,
      increment
    };
  },
  template: document.getElementById('appTemplate').innerHTML
};

const app = Vue.createApp({});
app.component('my-component', MyComponent)
app.mount(App, "#app");
```

### TL;DR;
- I didn't watch my template syntax and missed that a tag wasn't closed, which was causing the rendering not to work properly.
- should have spent more time reading the docs than trying madly to just get it to work.

## Resources :
- https://vue-composition-api-rfc.netlify.com/
- https://vue-composition-api-rfc.netlify.com/api.html
- https://github.com/vuejs/composition-api
- https://github.com/LinusBorg/composition-api-demos/
- https://github.com/vuejs/vue-next/blob/master/.github/contributing.md

Anyway, I'll be playing more with this in the future. I certainly subscribe to the benefits of the composition API, I just need to spend some more time understanding the how `ref`, `reactive`, `computed`, and `watch` all work together.