DEV Community

Manuel Ojeda
Manuel Ojeda

Posted on

Learn to create your custom Global State Management with Vue 3

Learn to create your custom Global State Management with Vue 3

Since Vue 3 Beta days we knew how awesome this framework was going to be, and since it annoucement we knew they did a monorepo making a lot of the feature from the framework available outside a Vue component, with this I mean:

import { /* Anything you need from Vue goes here */ } from 'vue'
Enter fullscreen mode Exit fullscreen mode

So we are going to use this advantage to create a simple but powerful Global State Management with a single few steps, so let's get started.


Let's create a simple app

For this example let use a simple example, a counter that comes for default using the amazing Vite.js, for this we need to run:

npm init @vitejs/app storex (or the name you want to use)
Enter fullscreen mode Exit fullscreen mode

After that, select vue

Select Vue

Then JavaScript or TypeScript, it will work in both:

Select JS or TS

Then follow the rest by changing into the recently created project and run npm install and open up the code in the the editor of your preference.


Creating the store

Let's begin with the store by creating a new folder inside the src folder by naming it, as you probably guess it, store and create a index.js inside the folder, the project directories should look like somethimg like this:

Creating the store directory

Once created the index file open it and place the next code:

import { reactive } from 'vue'

const store = ({
  state: reactive({
    count: null
  }),
  getters: {
    getCount() {
      return store.state.count
    }
  },
  mutations: {
    incrementCount() {
      store.state.count++
    }
  },
  actions: {
    initializeCount() {
      store.state.count = 0
    }
  }
})

export default store
Enter fullscreen mode Exit fullscreen mode

Let explain why the store is created like this:

import { reactive } from 'vue'

// We need to import the reactive function from Vue to make the
// global object reactive and get the treatment like this was Vuex
Enter fullscreen mode Exit fullscreen mode
const store = ({
  // Create a state with the reactive function we imported previously, this will manage the reactivity for us
  state: reactive({
    count: null 
    // If this is null is for the example,
    // of course you can initialize the
    // counter with 0 directly
  }),
  // This section will handle the getters
  getters: {
    getCount() {
      return store.state.count
    }
  },
  // This section will manage the changes into the state
  mutations: {
    incrementCount() {
      store.state.count++
    }
  },
  // This section will manage the actions needed for our store
  actions: {
    initializeCount() {
      store.state.count = 0
    }
  }
})
Enter fullscreen mode Exit fullscreen mode

And that's it, the store is created, now we need to use it in our app.


Using the Store in the App

Open main.js file in the root of src and change the code with this:

import { createApp } from 'vue'
import store from './store'
import App from './App.vue'

const app = createApp(App)
store.actions.initializeCount()

app.mount('#app')

/*
* By default the main.js comes like this:
* createApp(app)
* .mount(#app)
* We changed a little the behaviour by assigning the createApp 
* to the const app to avoid the mount before initializing the store
*/
Enter fullscreen mode Exit fullscreen mode

In App.js we need to change or add the store to track correctly in a global way when we are debugging/testing the application, so let's add the state as a computed property:

App.vue

If you want to use this sugar syntax, this all what you need, but be aware that the complete store is visible in the Dev Tools:

<script setup>
import { computed } from '@vue/runtime-core'
import HelloWorld from './components/HelloWorld.vue'
import store from './store'

const state = computed(() => store.state)
</script>
Enter fullscreen mode Exit fullscreen mode

The store is visible in the Dev Tools

As mentioned the store is completily visible in the dev tools

In case you want to make only the state visible you need to change the code like this but always using the Composition API:

<script>
import { defineComponent, computed } from '@vue/runtime-core'
import HelloWorld from './components/HelloWorld.vue'
import store from './store'

export default defineComponent({
  components: {
    HelloWorld
  },
  setup () {
    const state = computed(() => store.state)

    return {
      state
    }
  }
})
</script>
Enter fullscreen mode Exit fullscreen mode

State only visible in the dev tools

Only the state is visible in dev tools, so this is easily to debug when is needed

HelloWorld.vue

Open up HelloWorld.vue which can be found in the Components directory.

Once is ready to edit you need to change the code for this:

<template>
  <h1>{{ msg }}</h1>

  <p>
    <a href="https://vitejs.dev/guide/features.html" target="_blank">
      Vite Documentation
    </a>
    |
    <a href="https://v3.vuejs.org/" target="_blank">Vue 3 Documentation</a>
  </p>

  <!-- 
    You need to change the @click for the method created that
    will handle the change from the store
    Also change the count inside the {{  }}
  -->
  <button @click="increment">count is: {{ count }}</button>
  <p>
    Edit
    <code>components/HelloWorld.vue</code> to test hot module replacement.
  </p>
</template>

<!-- 
  Change the script from the sugar syntax 
  to the Oficial Composition API way
-->
<script>
import { defineComponent, computed } from 'vue'

// Import the store
import store from '../store'

export default defineComponent({
  props: {
    msg: String
  },
  setup () {
    // Send the count as a computed value from
    // the getters in the store
    const count = computed(() => store.getters.getCount())

    // This method will commit the change from the store
    const increment = () => {
      store.mutations.incrementCount()
    }

    return {
      count,
      increment
    }
  }
})
</script>

<style scoped>
a {
  color: #42b983;
}
</style>
Enter fullscreen mode Exit fullscreen mode

And we get this final result!

Our app is working

And that's it!!! We have created a simple but powerful Global State Management using the tools that Vue gives us and is fantastic what we can create from it, of course if you need advanced features I recommend to use Pinia or Vuex, but if you don't want to add more weight into your project this simple State Management should be enough.

Tell me what you think of this trick in the commentary and I hope is helpful for you, see around the next time.

See you soon

Latest comments (2)

Collapse
 
brojenuel profile image
Jenuel Oras Ganawed

how to do this in type script

Collapse
 
shayaulman profile image
Shaya Ulman

Thanks you!
This was very helpful!