Pineapple belongs in Vue: Using Stores in Vue app in 2023
Pineapple belongs in Vue: Using Stores in Vue app in 2023
Pineapple belongs in Vue: Using Stores in Vue app in 2023

Pineapple belongs in Vue: Using Stores in Vue app in 2023

Frontend

Pineapple belongs in Vue: Using Stores in Vue app in 2023

Frontend

Pineapple belongs in Vue: Using Stores in Vue app in 2023

Frontend

Pinia or Vuex? The choice was easier than expected.

Feb 21, 2023

Pinia is the new black

Vuex has been the go-to Flux-based library since the very first release of Vue. We’ve been shying away from using the Flux architectural pattern in our projects primarily due to string keys and state mutations. At least until recently, when we’ve discovered a library that’s an absolute joy to work with. And when it’s endorsed by the Vue aficionado himself, Evan You, we’re even more convinced to jump in.

We’re of course talking about Pinia.

A pineapple mascot is always a win in our book (Pinia)

Let’s start with the biggest advantage: While Vuex only offers a single store with additional modules, Pinia is modular by design. That means you can create multiple stores and import them to individual components however you see fit. Forget about writing your string mutations on Post-it notes and sticking them to your monitor. In Vuex, you can’t change state directly and have to rely on mutations, which is rarely worthy of celebration. Thankfully, Pinia allows for direct state changes, effectively wiping needless bureaucracy caused by dev tools.

Say goodbye to the convoluted props/event system

The primary use of store libraries lies in storing data from API. Having a single source-of-truth is more comfortable than digging for the latest data in each component. Later, we can split stores into categories, creating a separate store for tables, forms, etcetera.

But our global state doesn’t have to stop there. We can make things even easier by hooking the states of modals to our store. All communication through the event bus can go silent, and if a form opens up a modal window, it will connect directly to its store. By doing this, you mitigate issues caused by prop drilling and sending events over the entire application.

To illustrate…

While developing modern web applications, you’ll most likely have to maintain a large number of modals along with their state (open/closed). Therefore, creating an overseeing store for each component can be helpful.

export const useModalStore = defineStore('modal', () => {

    const modalOpen = ref(false)

    const open = (): void => {
    /* Anything we need to set when opening modal goes here */
    modalOpen.value = true
  }

    const close = (): void => {
    /* Anything we need to clear after closing modal goes here  */
    modalOpen.value = false
  }

    return {
        modalOpen,
        close,
        open,
  }
})

Wherever you decide to work with this modal, all it takes is importing its store. You’ll be able to alter its state without being limited by the distance from the modal component.

At first, this may seem unnecessary, especially when developing a smaller app with two components of the same level. Still, by splitting the function logic and HTML, your code will likely be much cleaner, especially in the component’s HTML.

In a model situation, when we’re coding a basic table with a form, the communication between them can sometimes be overlong and chaotic. Creating a store for each makes the code significantly easier to comprehend.

Communication through events:

<table-component>
	<template #detail="{ focusedItem }">
		<form :focusedItem="focusedItem" @cancel="formCanceled" @save="formCanceled"/>
	</template>
</table-component>

Communication through store:

<table-component
	v-model:focusedItem="tableStore.focusedItem"
	v-model:isDetailOpen="tableStore.detailOpen"
>
	<template #detail>
		<table-form/>
	</template>
</table-component>

In this case, we must also connect the table onto the store. Now we can alter the state of the table and form without cluttering our app’s code with props and events. As a result, the entire HTML flows smoother and offers enhanced readability.

Setup Store

After switching from options API in Vue2 to fresh script setup in Vue3, returning to the archaic option approach has become increasingly difficult. Thankfully, In Pinia, you can write an entire store as a setup function. A few changes will occur:

  • Refs become state properties

  • Computed become getter

  • Functions become actions

This approach allows us to include more logic that isn’t bound to the application. Cases in point — validation of individual entries or data-dependent watchers.

export const useModalStore = defineStore('modal', () => {

    const modalOpen = ref(false)

    const open = (): void => {
     /* Anything we need to set when opening modal goes here */
    modalOpen.value = true
  }

    const close = (): void => {
    /* Anything we need to clear after closing modal goes here  */
    modalOpen.value = false
  }

    watch(
		() => modalOpen.value,
		(val) => console.log('Modal je otevreny: ${val} ')
    )
    
   return {
       modalOpen,
       close,
       open,
  }
})

All of the above gives Pinia a sizeable advantage over its competitors, and we’re glad to see it become the new standard for all Vue projects. Even Vue CLI offers it as a default store instead of Vuex. Using Pinia is noticeably easier and cleaner than other Flux-based libraries. After all, adding features is much faster when you take design and data as two separate entities.