Brief Look on Vue 3.0 Composition API: More Readable  Components
Tags: dana, vue, javascript
Published: 2020-05-20 22:27:55

## The Options API
With the current Vue 2.x way of building components, we're separating it by the option, not feature. What this means is that for example, a single toggling state for `showTitle` will need to have a reactive data in `data` and a `method` to toggle the state. For a small simple component, this won't be a problem. But as the component grows, more functionality will be added, thus making reading the whole feature more difficult. An example can be seen below.

```jsx
<template>
  <div>
    <h2>Options API</h2>
    <p>{{ total }}</p>
    <p>
      Data:
      <span
        v-for="(n, idx) in apiRes"
        :key="idx">
        {{ n }}
      </span>
    </p>
    <input type="text" v-model="searchInputText"/>
    <p>Occurence: {{ results }}</p>
    <button
      @click="toggleAddForm">
      Add New Entry
    </button>
    <div v-if="showAddForm">
      <input type="text" v-model="newInputText"/>
      <button
        @click="add">
        Add
      </button>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      // #1 Search Form
      searchInputText: '',
      apiRes: ['Google', 'Amazon', 'Facebook', 'Uber', 'Netflix', 'Google', 'Twitter', 'Amazon'],
      results: 0,
      // #2 Input Form
      newInputText: '',
      showAddForm: false,
    }
  },
  computed: {
    // #1 Search Form
    total () {
      return `Total data: ${this.apiRes.length}`
    }
  },
  watch: {
    // #1 Search Form
    searchInputText (value) {
      this.results = this.apiRes.filter(itm => itm === value).length
    },
  },
  methods: {
    // #2 Input Form
    toggleAddForm () {
      this.showAddForm = !this.showAddForm
    },
    // #2 Input Form
    add () {
      if (this.newInputText) {
        this.apiRes.push(this.newInputText)
      }
    }
  },
}
</script>

```

Now that might still be readable to some extent, but as you can see we started to get different concerns of functionality split by `data` and `methods`. This will continue to get more complex when the component grows, let alone if we need to add `computed`  or lifecycle hooks.

Now on Vue 2.x, we have `mixins` that can address this concern. Mixins can be used for functionality purposes, making related data end methods in one file, then importing that file wherever it needed. For me, this still looks relevant and is the way to go for most cases (this post doesn't imply we should make it obsolete either). The downside on this, however, is that sometimes *we need to know what happens in it*, to decide if we can use it as-is, or we need to adjust it for our needs. We'll need to open the file, read again and when finished, check other places that use it making sure none of them broke. Other things are that there is a great potential that mixins can have similar names, which will make various issues.

## Composition API
The upcoming Composition API is the solution Vue 3 introduces to resolve the above issues. At the moment of writing (May 2020) it is still in beta, but we can already try it. It can make related data, methods, and functions scoped in one place instead of spread all over the file. We can use it in existing Vue 2.x projects by installing an additional npm package.

```jsx
npm i --save @vue/composition-api
```

Afterward, we will need to import the package in our `app.js` file as we can see below.

```jsx
import Vue from "vue";
import VueCompositonApi from "@vue/composition-api";
...
Vue.use(VueCompositonApi);
```

If you are starting from scratch with Vue 3.0 however, we can directly import everything in a .vue file from `vue`. For example:

```jsx
import { refs, reactive } from "vue";
```

We can now access the `setup()` function inside our `.vue` file. This function will run on component initialization, acts as a replacement to `beforeCreate` and `created` lifecycle hooks. Now let's see how we can use some of this enhancement compared to existing Options API in the previous Vue version.

## Data Reactivity
Reactivity is the main advantage when using Front End Javascript framework like Vue. In Vue 2.5 we are defining our data in `data ()` function or `data` object. In Composition API, we need to import `ref` or `reactive` from `@vue/composition-api` to achieve the same functionality.

```jsx
...
<script>
import { ref, reactive } from "@vue/composition-api"

export default {
	name: 'ComposedComponent',
	setup () {
		// Using ref, for simple values
		const searchInputText = ref('')
		const results = ref(0)
		// Using reactive, for object values
	  	const optData = reactive({
	    	displayTitle: true
	  	})

		// Accessing 
	  	const toggleDisplay = () => {
	     	optData.displayTitle = !compData.displayTitle;
	    	searchInputText.value = ! searchInputText.value;
	  	};
	
	  	return { results, searchInputText, toggleDisplay };
	}
}
</script>
```

Both `ref` and `reactive` can make a value reactive, but they have slight differences in use and access. `ref` can directly be assigned to a single variable or constants, while `reactive` can be used as the usual `data` function we use often in Vue 2.0, it will make the whole Object it covers to be reactive. If you can see above, `ref` will need `.value` for us to have access to its content, while `reactive` can be accessed directly.

Another way we can implement reactivity is by wrapping all values as an object in a `reactive` function. This way, if we need to have a computed value, we can directly access it without specifying `.value`. For example, we will use the above code and wrap the values in reactive function, then add a computed value which accesses the `result` value.

```jsx
...
<script>
import { ref, reactive, computed } from "@vue/composition-api"

export default {
	name: 'ComposedComponent',
	setup () {
		// Wrapping all values in a reactive function
		const allData = reactive({
			searchInputText: '',
			results: 0,
			resultText: computed(() => {
				return `Total result: ${allData.result}
			}),
		})

		// Accessing 
	  	const logData = () => {
	    	console.log(allData.resultText)
	  	};
		
	  	return { allData, logData }
	}
}
</script>
```

There is a drawback from this setup though. We will need to change how we access it in the template as well.

```jsx
<template>
  <div>
    <p>{{ allData.total }}</p>
    <p>
      Data:
      <span
        v-for="(n, idx) in allData.apiRes"
        :key="idx">
        {{ n }}
      </span>
    </p>
    <input type="text" v-model="allData.searchInputText"/>
    ...
  </div>
</template>
```

Naturally, if you are familiar with ES6, we will first think that we can just spread the object upon exporting like `return { ...allData, logData }`. But this will throw an error. Even if you specify it one by one like `allData.total`, the value will lose its reactivity.

For this, Vue 3.0 introduces `toRefs` that will do just this. The function will convert each of the object values and map it in their own `ref`. With this applied, we can access the values in the template like before.

```jsx
...
<script>
import { ref, reactive, computed, toRefs } from "@vue/composition-api"

export default {
	name: 'ComposedComponent',
	setup () {
		...
	  	return { ...toRefs(allData), logData }
	}
}
</script>
```

If we don't need to access any other than the reactive value, we can simply do `return ...toRefs(allData)`

## Computed and Watch
*Computed values* can be added by using `computed` function imported from Vue, similar to  `reactive`. It receives a function that returns the computed value as we have previously in Options API.
```jsx
import { computed } from '@vue/composition-api'
...
setup () {
    const apiRes = ['Google', 'Amazon', 'Facebook', 'Uber', 'Netflix', 'Google', 'Twitter', 'Amazon']
    const total = computed(() => {
      return `Total data: ${apiRes.length}`
    })
    return { total }
}
```
As for the Watch, we can assign a watch function using `watch`, also imported from `vue`. What we can do in there is similar to what we have in previous version.
```jsx
import { ref, computed, watch } from 'vue'
...
setup () {
	const results = ref(0)
	const searchInputText = ref('')
	watch(() => {
    results.value = apiRes.filter(itm => itm === searchInputText.value).length
    console.log(searchInputText.value)
  })
  return { results, searchInputText }
}
```

## Props
The `props` is similar to previous version. But in order to have it accessible in `setup` function, we need to pass it in as an argument. The *no-destructure* rule still applies here, as if we do it, we will lose the reactivity
```jsx
<script>
...
export default {
  props: {
    withPadding: {
      type: Boolean,
      default: false,
    },
  },
  setup (props) {
    const classNames = props.withPadding ? 'padded' : ''
    return { classNames }
  }
</script>
```

## File Management
Knowing above that much, some of us might think that this can make the `setup` function gigantic in no time. That goes in contrast with the readability improvement theme we have here. But fear not! As handy as we have mixins previously, we can also outsource related functions into separate files. They are functions after all.

**createCounter.js**
```jsx
import { reactive, computed, toRefs } from '@vue/composition-api'

export default function useEventSpace() {
  const event = reactive({
    capacity: 5,
    attending: ["Hey", "Some", "Name"],
    spacesLeft: computed(() => {
      return event.capacity - event.attending.length
    }),
  })
  function increase () {
    event.capacity++
  }
  return {
    ...toRefs(event),
    increase,
  }
}
```

**Page.vue**
```jsx
<script>
import createCounter from '@/../createCounter'
...
export default {
  setup () {
    return { ...createCounter() }
  }
</script>
```

## Emit
One change for `emit` in the new version is we are now encouraged to declare it in a separate `emits` property. This acts as a self-documentation of the code, ensuring that developers that come to a component they didn't make understand the relations to its parent.

Similar to props, we can also validate the payload passed and returns a Boolean as result.
```jsx
<script>
...
export default {
  // we can also pass an array of emit names, e.g `emits: ['eventName']`,
  emits: {
    inputChange: payload => {
      // payload validation
      return true
    }
  }
  ...
  mounted () {
    this.$emit('inputChange', {
      // payload
    })
  }
</script>
```

## Lifecycle Hooks
We can also set lifecycle hooks in `setup` function by importing `onXXX` beforehand. Important note on this one is we can't access `this` in setup function. If we still needed for example emit to parrent on mounted, using `mounted` hook from Options API seems to be the way for now.
```jsx
import { onMounted, onBeforeMount } from '@vue/composition-api'
...
export default {
  setup() {
    onMounted(() => {
	  console.log('Mounted')
    }
    onBeforeMounted(() => {
	  console.log('Before Mounted')
    }
  }
}
```

## Multi Root Template
As you may already know with Vue 2.x, we can only have one root element in the `template`. Not anymore in Vue 3.0 though. Thanks to the Fragments feature, having only one root element is no longer mandatory.

**Note**: *Your linter might complain about this being illegal. Mine does. Best to save this till we have proper release. But still exciting nonetheless* 
```jsx
<template>
  <div class="main-content">
    <p>{{ allData.total }}</p>
    ...
  </div>
  <div class="modal">
    <p>modal content</p>
    ...
  </div>
</template>
```

## Conclusions
Ease of code writing and readability seems to be one of the main focuses on the upcoming update of Vue. Apart from under the hood optimizations and better support of TypeScript, these are all exciting updates to look forward to. Especially they can be treated as add-on updates on an existing app rather, than complete rewrite, as the current API still supported.

There's so much more to the features listed as upcoming updates on the next version on Vue. you can see the complete list and updates over in Vue's RFCs repo here: [https://github.com/vuejs/rfcs](https://github.com/vuejs/rfcs).

Other features worth their in-depth article with detailed samples. More on that will be coming.

### Sources
> * **Vue Composition API Documentation**
> [https://composition-api.vuejs.org/api.html](https://composition-api.vuejs.org/api.html)
> * **Vue Mastery's Vue 3 Essential Course**
> [https://www.vuemastery.com/courses/vue-3-essentials/why-the-composition-api/](https://www.vuemastery.com/courses/vue-3-essentials/why-the-composition-api/)
> * **Academind's Video on Youtube**
> [https://www.youtube.com/watch?v=V-xK3sbc7xI](https://www.youtube.com/watch?v=V-xK3sbc7xI)
> * **Vue Composition API RFC**
> [https://vue-composition-api-rfc.netlify.com](https://vue-composition-api-rfc.netlify.com/)
> * **Emit RFC**
> [https://github.com/vuejs/rfcs/blob/master/active-rfcs/0030-emits-option.md](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0030-emits-option.md)
> * **Vue.js Developers - _Vue 3 Tutorial (for Vue 2 Users)_**
> [https://vuejsdevelopers.com/2020/03/16/vue-js-tutorial/](https://vuejsdevelopers.com/2020/03/16/vue-js-tutorial/)

### Image Credit
> [Markus Spiske on Unsplash](https://unsplash.com/@markusspiske?utm_source=medium&utm_medium=referral)