DEV Community

Cover image for How to use Composition API instead of Vuex in Vue3 and also in Vue2.
Kamil
Kamil

Posted on

How to use Composition API instead of Vuex in Vue3 and also in Vue2.

Why?

Often we develop some components that have access to a global state. For example, there are many separate components to filter some items, but these items have to be consistent. Or your application has user info, that has to use in different places. So, the main reason for using a global store (shareable reactive object) is just to apply more flexibility to your project.

Nowadays, Vuex has solved this problem very well. And it can do it today. In this article, we just discuss a new alternative and an interesting applying of a new composition-api.

Vuex

Before we dive into the Composition API let me to remind you about two main points of Vuex (and other Flux libraries that allow us to use shareable state in our app)

Our store has to be reactive. It means all templates of each component that use some variable from store has to update its template after variable update in store.
The only way of mutating shareable store is to execute mutations, some functions that work with the store. It's important for Vuex because it allows tracking all changes. Read more in docs

Composition API

Now, let's take a look at new features in Vue3 Composition API. There are tools to create real reactive objects like a Vuex store

There are ref and reactive. ref returns wrapped reactive object with value field. It's good for some alone variables. But reactive method returns a reactive object. And we can use it exactly like a state in Vuex. Actually, when you return an object from data() in your component, internally Vue uses the reactive method to make this object reactive.

Now we also can use it directly. Just import the reactive method from vue3 or if you still use Vue2 from a separate npm package @vue/composition-api. Yes, as you could see from title of the article we can already use it even in Vue2.

Npm package: @vue/composition-api

If you don't use Vue3 install @vue/composition-api as a plugin globally.

import Vue from 'vue'
import VueCompositionAPI from '@vue/composition-api'
Vue.use(VueCompositionAPI)
Enter fullscreen mode Exit fullscreen mode

Now we can use a reactive function in any component, but I advise you to create a separate file for our store.

If you already use Vue3 then just import reactive directly from vue. When you migrate to Vue3 you can just replace @vue/composition-api to vue

The whole complete project of example that is used in this article is in Sandbox

Vue2Vue3
// store.js
import { reactive } from '@vue/composition-api'
Enter fullscreen mode Exit fullscreen mode

Creating a reactive store

Let's create a new reactive store for searching by parameters. They will share across whole application and we can use it in any component.

// store.js
import { reactive } from "vue"

const state = reactive({
  keyWord: ""
})

export const store = {
  state
}
Enter fullscreen mode Exit fullscreen mode


js

Actually, we can use it right away in our components. Just add the store in each component that has to use the reactive object with property keyWord. For example, I made two identical components and place them into one another component. This is code for ComponentA and ComponentB (They have the same code)

<template>
  <div>
    <input v-model="store.state.keyWord" />
  </div>
</template>

<script>
import { store } from "../store";

export default {
  data() {
    return {
      store,
    };
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

We implemented the first point of Vuex, we have a reactive store and each template updates when something changes in the global store. You can play with it on the Sandbox.

One-Way Data Flow

As a Vue developer, you probably know that we have to avoid direct mutation of props. One-way Data Flow prevents child components from accidentally mutating the parent’s state, which can make your app’s data flow harder to understand (from Vue Docs) One-Way Data Flow in Vue Doc

Also, there is the second point of Vuex ideology: The only way of mutating sharable store is to execute mutations.

And, by the way, you should understand, we can work with our store now only because we import whole reactive store object and in data of our components it passes by link! We can't do the same "reactive" things with only one property. For instance, we can't do something like that:

data() {
  return {
    keyWord: store.state.keyWord
  }
}
Enter fullscreen mode Exit fullscreen mode

Now it's passed by value, and by the way, when we do this, we access the value and we can use it like value by default. But we can't change it in store. We have to change store.state.keyWord because the store is an object and it's passed by link. That's why our examples worked. You can also do the same trick with props, you can wrap it into an object and pass the whole object via props, BUT Vue will show you a warning about avoiding direct mutation props.

There are no warnings for changing reactive objects from another component. But I think according to One-Way Data Flow ideology and Vuex mutations we should apply this methodology to our examples. Let's use the best practices.

Mutations

So, let's create a special function that can change the state when we invoke this function. Add function changeKeyWord into the store.js file and add it into an export statement at the bottom of the file.

// store.js
import { reactive } from "vue"

const state = reactive({
  keyWord: ""
})

function changeKeyWord(value) {
  state.keyWord = value
}

export const store = {
  state,
  changeKeyWord
}
Enter fullscreen mode Exit fullscreen mode

Now we can use the changeKeyWord function in our components.

// ComponentA.vue & ComponentB.vue
<template>
  <div>
    <input :value="keyWord" @input="changeKeyWord" />
  </div>
</template>

<script>
import { store } from "../store";

export default {
  data() {
    return {
      store,
      keyWord: store.state.keyWord,
    };
  },
  methods: {
    changeKeyWord(evt) {
      this.store.changeKeyWord(evt.target.value);
    },
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

Now we have a local keyWord value in data from the store and we can change the value in the store by invoking the changeKeyWord function like Mutation in Vuex. But as you can notice, we can still change keyWord directly.

There is a good feature to disable direct mutation of our state. Special readonly function. It is also in composition-api. So we can just import it and use it in our store file.

// store.js
import { reactive, readonly } from "vue"

const state = reactive({
  keyWord: ""
})

function changeKeyWord(value) {
  state.keyWord = value
}

export const store = readonly({
  state,
  changeKeyWord
})
Enter fullscreen mode Exit fullscreen mode

Getters

Getters in Vuex are like computed properties in Vue. And composition-api allows us to use computed functionality directly. Just import it into our store.js file and create a computed function like a getter for our store.

// store.js
import { reactive, readonly, computed } from "vue";

const state = reactive({
  keyWord: "good"
});

function changeKeyWord(value) {
  state.keyWord = value;
}

const upperCasedKeyWord = computed(() => state.keyWord.toUpperCase());

export const store = readonly({
  state,
  changeKeyWord,
  upperCasedKeyWord
});
Enter fullscreen mode Exit fullscreen mode

Provide/Inject

We can use provide and inject to use store in all components without importing it in each file.

Just add this to your main component, usually, it's called App.js.

// App.js
import { store } from "./store";

export default {
  provide: {
    store
  }
}
Enter fullscreen mode Exit fullscreen mode

In our ComponentA.vue and ComponentB.vue remove import store and just add an inject

// ComponentA.vue & ComponentB.vue
<script>
export default {
  inject: ["store"],
  methods: {
    changeKeyWord(evt) {
      this.store.changeKeyWord(evt.target.value)
    }
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

The Whole complete project of example that is used in this article is in Sandbox

Conclusion

Composition API is a cool feature that can help us in different cases. It's flexible and powerful. But it doesn't mean that you have to replace Vuex with them. It's just a new option. Remember, Vuex is a time-tested plugin in the Vue ecosystem and it has many ready to use solutions. For example, there is an interesting plugin: vuex-history. It implements undo/redo functionality. Also, vue dev tools support Vuex. So, think twice about what to use.

Thanks a lot for reading. Please subscribe to my newsletter on this blog and on my youtube channel.

I was inspired by this article on dev.to that is written by Nonso Chukwuogor.

Read full article on my personal blog: https://www.kyle-ocean.dev/composition-api-instead-of-vuex-in-vue3-and-also-vue2

Top comments (0)