Advance State Management in Vue Js with Pinia

Advance State Management in Vue Js with Pinia

Vue.js is a popular JavaScript framework that allows developers to create dynamic and reactive user interfaces. It provides a simple and intuitive API that allows developers to build powerful web applications. One of the key aspects of building a complex web application is managing the application's state. State management is the process of managing and updating the data or state of an application. Vue.js provides several ways to manage and organize the state of an application, including the Vuex library.
However, Vuex has some limitations and complexities. It can be difficult to understand and use in large-scale applications, especially when the application state becomes more complex and requires advanced functionality.
To address these limitations, a new state management library called Pinia has been developed specifically for Vue.js. Pinia provides a simple and scalable solution for managing the state of Vue.js applications.
In this blog, we will explore the advanced features of Pinia and how to effectively use them in our Vue.js applications.


Getting Started with Pinia

To use Pinia in our Vue.js application, we need to install it first. We can do this by running the following command in our project directory:

npm install pinia

Once Pinia is installed, we can create an instance of Pinia in our main Vue.js file and use it to store and manage the application state. Here's an example of how to create a Pinia instance:

import { createPinia } from 'pinia';
import { createApp } from 'vue';
import App from './App.vue';

const pinia = createPinia();
const app = createApp(App).use(pinia);

app.mount('#app');

Defining Store in Pinia

In Pinia, the state is organized into stores. Stores are plain JavaScript objects that define the state and the actions that can be performed on that state.

To define a store, we can create a new file and export a Vuex-like store object using the defineStore function provided by Pinia. Here's an example of how to define a store in Pinia:

import { defineStore } from 'pinia';
import ApiResource from "@/Api/api"
import useRoute from "@/Composables/useRoute"

const route = useRoute()

export const useClientStore = defineStore('clientStore', {
  state: () => ({
    list: [],
  }),
  actions: {
    fetchData() {
      const api = new ApiResource(route("api.load.static.data"))
      const response = api.list()
      const { data } = response
      this.list = data.list
    },
  },
});

In this example which is implemented on the real world project, we define a store called clientStore with an initial state of the list: [] and one action: fetchData which will perform Ajax requests to the server and fetch the records. We will be using the Axios library behind the scenes. On the response variable, we will get the object that was passed from the server. After that, we used a Destructuring assignment to assign a response.data directly to data then we have finally assigned the data from the server to the local state list using this.list. So, now we need to call this function fetchData from the component so that it can assign the data to the state.

Notice that we can directly access and modify the state using this keyword.


Using Stores in Vue Components

Once a store is defined, we can use it in any Vue component by importing it and accessing its state and actions. To use a store in a component, we can use the useStore function provided by Pinia. Here's an example of how to use the client store we defined earlier.

Note: We will be using Composition Api in vue components instead of option Api.

<template>
  <div>
      <ul>
            <li v-for="(data,index) in clientStore.list"></li>
      </ul>
  </div>
</template>

<script setup>
import { useClientStore } from './ClientListStore';
import { onBeforeMount } from "vue"

const clientStore = useClientStore();

 onBeforeMount(() => {
    clientStore.fetchData()
 })

</script>

In this example, we import the useClientStore function from the ClientStore file and create an instance of the client store using the useClientStore function. Then, we can call the fetchData from our ClientStore file on our component using vue Lifecycle Hook onBeforeMount. This hook is to be called right before the component is to be mounted. We then bind the store's state list directly on the template.


Reactive State and Getters

The state of a Pinia store is reactive, which means that any changes to the state will automatically update any components that are using that state.
In addition to the state, Pinia also allows us to define getters. Getters are functions that derive and provide access to a computed value based on the state. Here's an example of how to define getters in a Pinia store:

import { defineStore } from 'pinia';
import ApiResource from "@/Api/api"
import useRoute from "@/Composables/useRoute"

const route = useRoute()

export const useClientStore = defineStore('clientStore', {
  state: () => ({
    list: [],
  }),
  actions: {
    fetchData() {
      const api = new ApiResource(route("api.load.static.data"))
      const response = api.list()
      const { data } = response
      this.list = data.list
    },
  },
  getters: {
    getItemById: (state) => (id) => {
      return state.list.find(item => item.id === id);
    },
  },
});

In this example, we define a getter called getItemById that computes the list on the state and finds the item that is passed down the parameter. We can then access the getItemById getter in our components like any other state property.


Using Multiple Stores

One of the advantages of using Pinia is the ability to use multiple stores in our Vue.js application. This helps to organize and modularize the state management of your application.
To use multiple stores, we can create and export multiple store objects using the defineStore function, just like we did earlier. We can also access the stores individually in your components using the useStore function.
Here's an example of how to use multiple stores in a Vue.js application:

import { createPinia } from 'pinia';
import { createApp } from 'vue';
import App from './App.vue';
import { useCounterStore } from './counterStore';
import { useUserStore } from './userStore';

const pinia = createPinia();
const app = createApp(App).use(pinia);

app.component('Counter', {
  setup() {
    const counterStore = useCounterStore();

    return {
      count: counterStore.count,
      increment: counterStore.increment,
      decrement: counterStore.decrement,
    };
  },
});

app.component('User', {
  setup() {
    const userStore = useUserStore();

    return {
      fetchUser: userStore.fetchUser,
      user: userStore.user,
    };
  },
});

app.mount('#app');

In this example, we create two separate stores: counterStore and userStore. We then use the useCounterStore and useUserStore functions to create instances of each store in separate components.


Advantages and Disadvantages of Pinia :

Pinia is a state management solution for Vue applications, designed as an alternative to Vuex. As with any library or framework, it comes with its advantages and disadvantages.

Advantages :

  • Simplicity and Developer Experience: Pinia offers a more straightforward API than Vuex, making it easier to set up and use.

  • TypeScript Support: Pinia provides built-in TypeScript support, which makes it a good choice for projects where type safety is a priority.

  • Improved DevTools: Pinia has its own DevTools extension, which provides a cleaner interface and a more informative view of your stores than the Vuex DevTools.

  • No Namespacing Required: Unlike Vuex modules which often require namespacing for actions, mutations, and getters, Pinia stores are inherently namespaced by their unique IDs.

  • Decoupled from Vue Core: Pinia isn't tightly coupled with Vue Core, which means it might adapt more quickly to changes or new patterns in the Vue ecosystem.

  • Size: Pinia is smaller in terms of bundle size compared to Vuex.

  • Direct Access to State: Instead of committing mutations, you can directly change the state in Pinia, which some developers find more intuitive.

Disadvantages :

  • Newer Library: Being a newer library compared to Vuex, it might not be as battle-tested in large-scale applications. There might be fewer resources, tutorials, and solutions available for common problems than Vuex.

  • Migration: If you have an existing project with Vuex, migrating to Pinia might require a considerable amount of work.

  • Plugin Ecosystem: Vuex has a wider range of plugins available due to its longer existence. Depending on your needs, you might find a lack of plugins for Pinia or have to wait for the community to create equivalent plugins.

  • Familiarity: Since Vuex has been the de facto state management solution for Vue for a long time, many Vue developers are more familiar with Vuex. Adopting Pinia might introduce a learning curve for such teams.

  • Might not be Necessary for Small Projects: For small-scale projects, introducing Pinia might be overkill. Vue 3's Composition API already provides reactive primitives (ref and reactive) which can handle state management for smaller apps without the need for a dedicated store solution.


Conclusion

Pinia is an advanced state management library for Vue.js that provides a simple and scalable solution for managing the state of your Vue.js applications.
In this blog, we explored the advanced features of Pinia, including defining stores, using stores in Vue components, reactive state and getters, fetching data with actions, and using multiple stores.
By using Pinia, we can take full advantage of Vue.js's reactive nature and build powerful and robust applications with ease.