What I Have Learned So Far about 'Vue-Composition-API'
Tags: vue, javascript
Published: 2020-02-07 14:58:20

> **"What I Have Learned So Far" Edition also known as sequence of series that i am personally developing for my writing routine. Originated from certain topic or reading material lately that I enjoy, cheers 🥂.**

After following some news about vue lately, My eyes 👀 caught by the new interesting thing that will come and which is a major update from Vue 2 to Vue 3 expected release in mid to end term this year. One of the significant transformations is in the new alternative code structure from the options API (previous way) and then the composition API (new optional way).


| Options API        | Composition API           |
| ------------- |:-------------:| 
| - Intuitive and easy to get started with     | - Provides more flexible code organization & logic reuse capabilities |
| - Scalability issues in large applications      | - Can be use alongside options API     |  

Then here are some reasons and a comparison between composition and options according to Evan You (The creator of Vue JS) and the image below shows the different code structures of options and composition API.

![Alt Text](https://user-images.githubusercontent.com/499550/62783026-810e6180-ba89-11e9-8774-e7771c8095d6.png)

*original Source : vue-composition-api-rfc.netlify.com (vue-composition-documentation)*


## 1. Getting Started
I have used a simple implementation called 📝 refrigerator-note 📝 mainly using [@vue/composition-api](https://www.npmjs.com/package/@vue/composition-api) and [vue-cli](https://www.npmjs.com/package/@vue/cli) for this topic and here is the [Github repo](https://github.com/dinulsyah/vue-composition). You can clone it and run several commands like this on your local machine:
* *yarn install / npm install* (install the dependency)
* *yarn serve / npm run serve* (running the application)

The vue2 code structure is placed in the [master](https://github.com/dinulsyah/vue-composition) branch and [composition](https://github.com/dinulsyah/vue-composition/tree/vue-composition) in the api-composition branch and here is a list of existing [users and passwords](https://github.com/dinulsyah/vue-composition/blob/vue-composition/src/users/index.js) if you want to try accessing the [online demo](http://vue-composition.dinulsyah.com/)/local machine lastly don't forget to leave a star for the repo if you like it 🌟

```javascript
// src/main.js
import Vue from 'vue'
import App from './App.vue'
import VueCompositionApi from '@vue/composition-api'
import Miligram from 'milligram'
import Vmodal from 'vue-js-modal'

Vue.config.productionTip = false
Vue.use(VueCompositionApi)
Vue.use(Miligram)
Vue.use(Vmodal)

new Vue({
  render: h => h(App),
}).$mount('#app')
```

## 2. Setup
The composition API requires a setup initialization that will return an object that wraps state, methods, computed, watch , etc to expose this variables/functions to the templating Vue. And also note that ***this*** statement cannot be used in the setup.

```vue
// src/components/RefrigeratorLoginForm.vue

// Vue 2 Structure

<template>
  ....
</template>

<script>
export default {
  name: 'RefrigeratorLoginForm',
  props: {
     ...
  },
  data(){
    return{
      ...
    }
  },
  computed:{
    isDisable:function(){
      ...
    }
  },
  methods:{
    loginForm(){
      ...
    }
  }
}
</script>

<style scoped>
</style>

// Vue Composition Structure

<template>
  ....
</template>

<script>
export default {
  name: 'RefrigeratorLoginForm',
  props: {
    loginMethod: {
      ...
    }
  },
  setup(props){
    ....

    return{
      username,
      ....
    }
  }
}
</script>

<style scoped>
</style>

```

## 3. Local/Component State
In the composition API, there are two ways to explicitly initiate a local state with **ref** or **reactive** that observe for each state change, the ref takes parameters without being wrapped into an object while reactive uses the object data type and assign a specific key. These two are optional but there are some differences in where to access the ref state in the setup function with the *variablename.value* and the *variablename* in the Vue template. Whereas reactive is also accessed by the assign key name in the setup and the template so it kind of makes senses in my opinion.

```vue

// src/components/RefrigeratorNoteForm.vue

// Vue 2 Structure

<template>
  <div>
  <input class="button-clear button-small float-right" type="submit" value="Log Out" @click="logOutNow">
  <div class="component">
    <input class="button button-clear" type="submit" value="---> See List Of Note <---" @click="toListNote">
    <h3 class="center">Send Your Note</h3>
    <form @submit.prevent="addNote">
    <fieldset>
      <label for="nameField">Send To</label>
      <select id="availableUser" v-model="selectedUser">
        <option value="" selected>Select Available User</option>
        <option :value="user" :key="user" v-for="user in allUsers">{{ user }} </option>
      </select>
      <label for="nameField">Note</label>
      <input type="text" :placeholder="placeholder" id="nameField" v-model="inputNote">
      <input class="button-black float-right" type="submit" value="Write" :disabled="isDisable">
    </fieldset>
    </form>
  </div>
  </div>
</template>

<script>
import { Users } from '../users';
export default {
  data(){
    return{
      placeholder:'',
      allUsers:[],
      inputNote:'',
      minNote:10,
      username:'',
      selectedUser:''
    }
  },
  props:{
    toSection:{
      type:Function,
      required:true
    },
    sendNote:{
      type:Function,
      required:true
    },
    logoutMethod:{
      type:Function,
      required:true
    }
  },
  computed:{
    isDisable:function(){
      return !(this.inputNote.length > this.minNote && this.selectedUser !== '');
    }
  },
  methods:{
    toListNote(){
      this.toSection({section:'on-note'})
    },
    addNote(){
      this.sendNote({
        from:this.username,
        sendTo:this.selectedUser, 
        note:this.inputNote
      })
      this.selectedUser = ''
      this.inputNote = ''
    },
    logOutNow(){
      this.logoutMethod()
    }
  },
  mounted(){
    this.username = Users[localStorage.getItem('username')]['name']
    this.placeholder = `life-and-death Note From ${this.username}`
    this.allUsers = Object.keys(Users)
      .filter(user => user !== localStorage.getItem('username'))
      .map(user => Users[user]['name'])
  }
}
</script>

<style scoped>
...
</style>


// Vue Composition Structure

<template>
  <div>
  <input class="button-clear button-small float-right" type="submit" value="Log Out" @click="logOutNow">
  <div class="component">
    <input class="button button-clear" type="submit" value="---> See List Of Note <---" @click="toListNote">
    <h3 class="center">Send Your Note</h3>
    <form @submit.prevent="addNote">
    <fieldset>
      <label for="nameField">Send To</label>
      <select id="availableUser" v-model="selectedUser">
        <option value="" selected>Select Available User</option>
        <option :value="user" :key="user" v-for="user in allUsers.data">{{ user }} </option>
      </select>
      <label for="nameField">Note</label>
      <input type="text" :placeholder="placeholder" id="nameField" v-model="inputNote">
      <input class="button-black float-right" type="submit" value="Write" :disabled="isDisable">
    </fieldset>
    </form>
  </div>
  </div>
</template>

<script>
import { Users } from '../users';
import { reactive, ref, computed, onMounted } from '@vue/composition-api'
export default {
  name: 'RefrigeratorNoteForm',
  props:{
    toSection:{
      type:Function,
      required:true
    },
    sendNote:{
      type:Function,
      required:true
    },
    logoutMethod:{
      type:Function,
      required:true
    }
  },
  setup(props){
    let placeholder = ref('')
    let allUsers = reactive({
      data:[]
    })
    let selectedUser = ref('')
    let inputNote = ref('')
    let minNote = ref(10)
    const addNote = () => {
      props.sendNote({
        from:username.value,
        sendTo:selectedUser.value, 
        note:inputNote.value
      })
      selectedUser.value = ''
      inputNote.value = ''
    }
    let username = ref()
    const logOutNow = () => {
      props.logoutMethod()
    }
    const isDisable = computed(() => {
      return !(inputNote.value.length > minNote.value && selectedUser.value !== '');
    })
    const toListNote = () => {
      props.toSection({section:'on-note'})
    }
    onMounted(() => {
      username.value = Users[localStorage.getItem('username')]['name']
      placeholder.value = `life-and-death Note From ${username.value}`
      allUsers.data = Object.keys(Users)
        .filter(user => user !== localStorage.getItem('username'))
        .map(user => Users[user]['name'])
    })
    return {
      placeholder,
      allUsers,
      inputNote,
      selectedUser,
      isDisable,
      addNote,
      toListNote,
      logOutNow
    }
  }
}
</script>

<style scoped>
...
</style>

```

## 4. Methods, Computed, Props, Watch, LifeCycle Hooks
The **Methods** written as a usual function in Javascript

While **Computed** calling Computed from Vue-Composition then passing a function as parameters stored the expected result in a variable that implicitly has been observed by Vue so that to access it we need to use *variablename.value*.

**Props**, Same as a previous structure in Vue 2 but props are received from the first parameter setup function.

Importing **OnMounted**(Life Cycle Hooks) and **Watch** from composition-API then like Vue 2 for onMounted are called when the template has been rendered and we can access data, components, global objects, etc. While Watch will be called if there is a change in the watch state and then perform a logical process by passing again a function on OnMounted or Watch.

```vue
// src/App.vue

// Vue 2 Structure

<template>
 
</template>

<script>
import RefrigeratorLoginForm from './components/RefrigeratorLoginForm.vue'
import RefrigeratorNoteForm from './components/RefrigeratorNoteForm.vue'
import ListNoteTable from './components/ListNoteTable'
import { Users } from './users'
export default {
  name: 'app',
  components: {
    RefrigeratorLoginForm,
    RefrigeratorNoteForm,
    ListNoteTable
  },
  data(){
    return{
      noteList:[],
      onPage: null,
      logoutState:false
    }
  },
  methods:{
    loginRefrigerator({username, password}){
      (Users[username] && Users[username]['password'] === password) 
        ? this.setLogin(username) 
        : this.modalShow()
    },
    addNoteToList({from,sendTo,note}){
      this.noteList.push({
        from,
        sendTo,
        note
      })
    },
    deleteNote(index){
      this.noteList.splice(index,1)
      this.$modal.show('delete')
    },
    setLogin(username){
      this.onPage = 'on-login';
      this.username = username;
      localStorage.setItem('section',this.onPage)
      localStorage.setItem('username',this.username)
    },
    changeSection({section}){
      this.onPage = section
      localStorage.setItem('section',section)
    },
    modalShow(){
      this.$modal.show('error-login');
    },
    logoutRefrigerator(){
        this.logoutState = true
    },
    modalLogout(){
      this.$modal.show('logout');
    },
  },
  watch:{
    logoutState:function(newState){
      if(newState){
        this.onPage = 'on-no-login'
        localStorage.clear()
        this.modalLogout()
        this.logoutState = false
      }
    }
  },
  created(){
    (Users[localStorage.getItem('username')]) 
      ? (this.onPage = localStorage.getItem('section')) 
      : this.onPage = 'on-no-login'
  }
}
</script>


// Vue Composition Structure

<template>
  ....
</template>

<script>
import RefrigeratorLoginForm from './components/RefrigeratorLoginForm.vue'
import RefrigeratorNoteForm from './components/RefrigeratorNoteForm.vue'
import ListNoteTable from './components/ListNoteTable'
import { reactive, ref, onMounted, watch } from '@vue/composition-api'
import { Users } from './users'
export default {
  name: 'app',
  components: {
    RefrigeratorLoginForm,
    RefrigeratorNoteForm,
    ListNoteTable
  },
   setup(props,context){
    let noteList = reactive({
      data:[]
    })
    const addNoteToList = ({from,sendTo,note}) => {
      noteList.data.push({
        from,
        sendTo,
        note
      })
    }
    const deleteNote = (index) => {
      noteList.data.splice(index,1)
      context.root.$modal.show('delete')
    }
    let onPage = ref(null)
    const changeSection = ({section}) => {
      onPage.value = section
      localStorage.setItem('section',section)
    }
    let username = ref('')
    const setLogin = (user_name) => {
      onPage.value = 'on-login';
      username.value = user_name;
      localStorage.setItem('section',onPage.value)
      localStorage.setItem('username',username.value)
    }
    const loginRefrigerator = ({username, password}) => {
        (Users[username] && Users[username]['password'] === password) 
        ? setLogin(username) 
        : modalShow()
    }
    let logoutState = ref(false)
    const logoutRefrigerator = () => {
        logoutState.value = true
    }
    const modalLogout = () => {
      context.root.$modal.show('logout')
    }
    watch(() => {
      if(logoutState.value){
        onPage.value = 'on-no-login'
        localStorage.clear()
        modalLogout()
        logoutState.value = false
      }
    })
    const modalShow = () => {
      context.root.$modal.show('error-login')
      //this.$modal.show('error-login')
    }
    onMounted(() => {
      (Users[localStorage.getItem('username')]) 
      ? (onPage.value = localStorage.getItem('section')) 
      : onPage.value = 'on-no-login'
    })
    return{
      noteList,
      onPage,
      loginRefrigerator,
      addNoteToList,
      deleteNote,
      changeSection,
      logoutRefrigerator
    }
  }
}
</script>

```

## 5. Structure and Use Composable
Vue composition supports the code structure for better readability and ease of maintenance in my opinion, we can also use the composable function to group domains separately in files and then call them or combine them with other composable functions.


```js
// src/cmp-fun/ListNoteTable.js
  
export default function(props){
    const toAddNote = () => {
        props.toSection({section:'on-login'})
    }

    const deleteList = (index) => {
        props.deleteNote(index)
    }
    
    return {
        toAddNote,
        deleteList
    }
}
```
```vue 
// src/components/ListNoteTable.vue
<template>
  <div class="component">
      <div class="float-right">
        <input class="button button-clear" type="submit" value="-> Back To Write Your Note" @click="toAddNote">
      </div>
       <h3 class="center">List Of Notes</h3>
        <table>
            <thead>
                <tr>
                    <th>From</th>
                    <th>Send To</th>
                    <th>Note</th>
                    <th>Action</th>
                </tr>
            </thead>
            <tbody>
                <tr :key="index" v-for="(note,index) in listNote">
                    <td>{{note.from}}</td>
                    <td>{{note.sendTo}}</td>
                    <td>{{note.note}}</td>
                    <td>
                        <button class="button button-outline button-small" @click="deleteList(index)">Delete</button>
                    </td>
                </tr>        
            </tbody>
        </table>
  </div>
</template>

<script>
import useListNote from '../cmp-fun/ListNoteTable'
export default {
    props:{
        toSection:{
            type:Function,
            required:true
        },
        listNote:{
            type:Array,
            required:true
        },
        deleteNote:{
            type:Function,
            required:true
        }
    },
    setup(props){
       return useListNote(props)
    }
}
</script>

<style scoped>
...
</style>
```

## 6. Accessing Plugin through Context
Due to not being able to access ***this*** statement on the Vue composition, one way to access global object or plugins that we usually use like ***this.$modal*** is with the params context in the second parameter and then in the setup function and call it like so ***context.root.$modal***.

```vue
// src/App.vue
<template>
 ...
</template>

<script>
...
 
export default {
  ....
  setup(props,context){
    ....

    const modalLogout = () => {
      context.root.$modal.show('logout')
    }

    return{
      ...
    }
  }
}
</script>

```



*Vue Composition in Vue 3 later remains a choice that is not mandatory but also intended to tackle the problem for large scale application scalability and maintainability with long maintenance and life span.Another interesting thing denoted that the composition/options API can be used together according to the desired requirements. So are you impatient to anticipate the release of Vue 3 or other great things coming from Vue ?*