DEV Community

Cover image for From my point of Vue: State management
Jesus Guerrero
Jesus Guerrero

Posted on • Originally published at jesusantguerrero.com

From my point of Vue: State management

While working with medium/large sized apps you will find the need to have some data, functionality or logic available globally in a single source of truth because you need them in different components and pages across the app, it could be: user data, settings, permissions, etc. Sounds familiar? We refer to the solutions to address this problem as State Management.

In this post we are going to review some use cases when we might need a state management solution and the options we have in Vue to work with them.

Some use cases are:

  • Sharing data that are used deeply in components
  • Sharing global state
  • Working with SSR

Sharing data that are used deeply in components

Since vue 2 the framework provide an alternative to props to pass down data to a deep child without the need of a state manager library. This is the provide/inject in the composition API those functions are available and ready to share reactivity too.

We can find an Illustration for the idea in this picture from Vue documentation:

provide/inject idea

Let's see how the code will look like for the following use case:

  • We have a main layout that have a selectedDate and a component WeekContainer that have the last 7 days rendered as an individual component WeekDay component, we need the selectedDate data from the parent to see if one of the WeekDays is the selected one.

Components Schema:

  -- Parent [selectedDate]
  --- WeekContainer
  ---- WeekDay [needs selected day]
Enter fullscreen mode Exit fullscreen mode
<script setup>
/** Parent **/

import { provide } from "vue"
const selectedDate = ref(new Date())
</script>

<template>
 <!--...-->
</template>
Enter fullscreen mode Exit fullscreen mode
<script setup>
/** WeekContainer **/
</script>

<template>
 <div v-for="day in week" :day="day"> 
</template>
Enter fullscreen mode Exit fullscreen mode
<script setup>
/** WeekDay Component **/

import { inject, computed } from "vue"
const props = defineProps({
  date: {
    type: Date,
    required: true
  } 
})

const selectedDate = inject('selectedDate')
const isSelectedDate = computed(() => selectedDate.value == props.date)
</script>

<template>
 <!--...-->
</template>
Enter fullscreen mode Exit fullscreen mode

Live example
live code

Sharing global state

The second case is maybe one of the most common, In general if our application requires authentication, permissions and settings eventually we are going to need to have access to these data without the need to make an API call each time we need them.

Here we can combine some techniques to avoid an external library with vue 3 and the Composition Api we can use an reactive object to store the data an access to them when we need it. I wrote about this concept previously in build and auth flow with auth0 and Vue 3

Let's see some code:

Here we are exporting a reactive object with Auth Information

/** AuthState.js **/
import { reactive } from 'vue';

export const AuthState = reactive({
    user: null,
    loading: false,
    isAuthenticated: false,
    provider: null,
    // settings?
});
Enter fullscreen mode Exit fullscreen mode

Next we can build a function that interacts with our AuthState to set the user in case of login/registration an unset in case of logout.

export const useAuth = (state) => { 
 // The implementation will go here

  return {
     login,
     logout,
     init
  }
}
Enter fullscreen mode Exit fullscreen mode

Then in the main App.vue we can import our functions to setup the initial state. Then we an import the AuthState anywhere.

<script setup>
import { useAuth, AuthState } from "./utils/useAuth";
const { login, logout, init } = useAuth0(AuthState);

init();
</script>

<template>
  <div v-if="!AuthState.loading">
    <img alt="Vue logo" src="./assets/logo.png" />
    <div v-if="!AuthState.isAuthenticated">
      <button @click="login()" class="btn btn-primary">Login</button>
    </div>

    <div v-else>
      <p> Welcome to VueAuth <strong>{{ AuthState.user.name }}</strong></p>
      <button @click="logout()" class="btn btn-secondary">Logout</button>
    </div>
  </div>

  <div v-else>
    Loading ...
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

You can check a complete code applying this technique here and applied to an app running in the wild zen

Working with SSR

We are heading to our last but not least use case, with Server Side Rendering our app have some special needs so far At this point the minimal requirement is sharing state between the server side generated content and the frontend once is hydrated.

In that case we can go with a library like pinia (the spiritual successor of Vuex🙏🏽). It gives us a SSR compatibility, modular and intuitive design in a light package with devtool support.

Usage: Declaring the store

// stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => {
    return { count: 0 }
  },
  // could also be defined as
  // state: () => ({ count: 0 })
  actions: {
    increment() {
      this.count++
    },
  },
})
Enter fullscreen mode Exit fullscreen mode

Once it is declared you can use it in your components:

import { useCounterStore } from '@/stores/counter'

export default {
  setup() {
    const counter = useCounterStore()

    counter.count++
    // with autocompletion ✨
    counter.$patch({ count: counter.count + 1 })
    // or using an action instead
    counter.increment()
  },
}
Enter fullscreen mode Exit fullscreen mode

Wrapping up

  • You can handle state management in Vue 3 with provide/inject, composables or store libraries like pinia

  • They might help you with these use case:

    • Sharing data that are used deeply in components
    • Sharing global state
    • Working with SSR

I hope you find it useful, let me know about any questions you opinions you have Twitter or even share how you handle those use cases in the comment section.

Thanks for reading and have a nice day.

Resources

Top comments (0)