Proven Pinia Patterns

Rate this content
Bookmark

With Vue's new-and-improved state management library, Pinia, we gain a much more modular tool. While being more flexible, leaner, and lacking the Mutations of Vuex, Pinia presents us with more opportunities to be creative, for better or worse, with our app architecture and how state management is conducted and organized within it.

This talk explores some @posva-approved best practices and architectural design patterns to consider when using Pinia in production.

20 min
15 May, 2023

Video Summary and Transcription

Pinnia is a lighter, more modular state management solution for Vue apps, offering consistent patterns, TypeScript support, and an intuitive developer experience. PINIA encourages splitting state into manageable domains, improving code splitting, type inferences, team collaboration, and debugging clarity. Pinia provides flexibility in accessing and mutating state, with different approaches for option and setup stores. It also offers features like patch, reset, and onAction methods. Vue Mastery offers comprehensive Pinnia courses and resources for fast learning.

Available in Español

1. Introduction to Pinnia and its Features

Short description:

Hello, I'm Adam Jarr, co-founder of Vue Mastery. Today, we'll explore Pinnia, a lighter, more modular state management solution for Vue apps. We'll cover organizing stores, options vs. setup stores, accessing and updating state, and unique Pinnia features like patch and reset. We'll use a demo app, a Restaurant Finder, to illustrate these concepts. Let's dive in!

Hello, my name is Adam Jarr. I am co-founder of Vue Mastery, the ultimate learning resource for Vue developers. Through our collection of Vue courses, you can level up your skills, elevate your code and become the best Vue developer you possibly can be.

So in today's talk, we're going to take a journey deep into the Vue ecosystem to explore the planet of Pinnia. As we should all be aware by now, Pinnia is the evolution of where VueX was, bringing a newer, lighter weight, more modular, less prescriptive, more freeing version of state management into our Vue apps. And as the saying goes, with great freedom comes great responsibility. And we have added responsibility now because we can get creative about how we implement state management in our Vue apps. And when we do this well, we can use Pinnia to develop very elegantly architected apps, avoiding anti-patterns, and creating an application whose state can scale as we scale.

And that brings us to the focus of today's talk, where we unpack the proven Pinnia patterns that you can use confidently throughout your apps, because this talk and its contents were approved by Posva, or Eduardo San Martín Marrote, the creator of Pinnia. So by the end of this talk, we will have covered organizing our Pinnia stores, options versus setup stores, how they differ, which one you might want to choose for what use case, accessing and updating your Pinnia state. And we'll also explore some unique Pinnia features such as patch and reset. Throughout this talk, we're going to be looking at a demo app so we can apply and unpack these features in a more real-world use case. And as you can see, this is a Restaurant Finder. You're going to type in the city and search Sherm to find restaurants within a certain area, and the user can register an account. They can save their favorites, and they can read information pulled from the Google Maps API, such as ratings and reviews. This app is going to be tracking each of its logical concerns globally with its own Pinnia stores, and we're going to cover all this throughout the talk, so let's get started.

2. Why Use Pinnia and Its Features

Short description:

Vue 3's composition API provides a basic state management solution, but Pinnia offers consistent patterns for collaborative organization, SSR security, DevTools for debugging, TypeScript support, and an intuitive developer experience. If you need these features, pinya is the way to go.

Before we dive deeper into Pinnia concepts, let's first get clear on why you would actually want to use Pinnia in the first place, because Vue 3 already has the composition API with a built-in reactivity system with the flexibility for sharing and reusing state. Just using the composition API, we could create a reactive object to serve as a store to manage our global state. Then we would just import that store into whichever component needs it, and because of how Vue's reactivity system works, any component that imports that store can directly mutate its global state. And if that state were to change and it had a template that were displaying that state, that template would reactively change as well.

So again, if we can just use the composition API to create a minimal, fundamental version of state management in a Vue 3 app, then when exactly would we need a state management library such as pinya? Well, it's going to come in handy when you want to have consistent patterns for collaborative organization. This is especially important for large teams that are collaborating on a large-scale application. One such pattern that you might want to follow collaboratively together is a predictable way for you to mutate state and the actions within pinya allow us to achieve that. SSR security is another thing to consider and why you might want to use pinya because when you're using server-side rendering, you'll need to be careful with how you manage global state since SSR apps, they initialize the application modules on the server and then share that state across every request. So this could lead to security vulnerabilities, but pinya was designed to make it safer and easier to manage state in those server-side rendered applications. Additionally, when using pinya, we get the DevTools and all the transparency and helpful insight that those tools can provide us. This is going to make debugging and really understanding the state of our applications a lot clearer. Increasingly, it's becoming important for JavaScript developers to be working with tools that have TypeScript support, and pinya has first-class support for using TypeScript. All of these reasons combined create for a smooth and intuitive developer experience when you're using pinya to manage state in your applications. So, if your needs include any of the things that I laid out in this list here, you're probably going to want to decide to use pinya instead of just relying on the Composition API for your state management needs. So all of that to say, when you use pinya, you're going to be empowered with a robust and refined way to manage the state in your application, architect it in a way that you can be able to debug and collaborate nicely amongst your team. So, now that we're clear on when exactly we would want to use pinya, let's start to explore those features.

3. Defining Pinya Stores and Their Differences

Short description:

When using Vue 3 and pinya, you can choose to define stores as options or setup stores. The structure of an option store is similar to the Options API, while a setup store is more akin to the Composition API. Setup stores offer advantages like easy integration with composables and watchers. In a demo app, we used a setup store called geolocation that relied on the usegeolocation composable to fetch the user's coordinates and trigger actions to fetch location data from the Google Maps API.

When you're using Vue 3, you can choose to use the Options API or the Composition API. Similarly, when you're using pinya, you can define your stores as options or setup stores. Setup is going to refer to the setup function within the Composition API version of a pinya store. And there's really no right or wrong answer for which one you should be using in your apps, but let's explore the ways that they differ so that you can see which one might be better for your specific use case.

First, let's review how we define each type of pinya store. Anytime we want to create a store, whether it's an option store or a setup store, we must first import define store from pinya. Then we pass in a string for the name of the store as the first parameter. And this string needs to be unique because this is what pinya is going to be using to track the state of your store. And it's also what you're going to see in the dev tools. It's also going to be helpful when you start to use plugins in pinya. So just be very mindful to have a unique and nice understandable name for each of your stores as you're defining them.

Now, let's take a look at the structure of an option store. Not coincidentally, this kind of store takes in an object as the second parameter of the define store function. The state actions and getters here are really analogous to the data methods and computer properties when you're writing a component with the options API. Alternatively with a setup store, the second parameter that you're feeding into define store, that is going to be a function. This kind of store will feel familiar to how you'd compose a component with the composition API. We create state with ref or reactive. Our action is simply a function and our getter is a computed property. And note how we are returning everything here that we want to be accessible to other components. This is also gonna make everything here that is returned trackable by the dev tools, so just make sure you don't forget to do that.

Ultimately, like I mentioned earlier, whether you use options stores, setup stores or options end setup stores in your same application that is gonna be up to you and your team, but I did want to explore some of the ways that setup stores do have advantages. So instead of stores allow us to take advantage of the view3 reactivity system, that's gonna mean we can easily use composables in those stores, like composables from the view use library. And we can also use watchers easily in them as well. We actually use both of those in the demo app that I referenced in the beginning of the talk. So let's take a look at that. Notice this behavior here, where if the user doesn't type anything into the city input, their current location will be found automatically. Looking at the code inside of a store within this project that is called geolocation. We can see that it is indeed a set up store and its functionality relies on the usegeolocation composable from view use. It's using that to get the coordinates of our user. And then meanwhile, the watchers keeping an eye on those coordinates to trigger an action to fetch the location data from the Google Maps API.

4. PINIA Modularity and Store Organization

Short description:

PINIA is modular by design, encouraging the splitting of state into manageable domains. Unlike Vue X, PINIA allows the creation of stores for each major logical concern, improving code splitting, type inferences, team collaboration, and debugging clarity.

So again, we're able to use both that composable and the watcher within this set up store. Now, it's important to understand that PINIA is modular and it's modular by design. That means that we are encouraged by its very API to split up our state into manageable domains. This compares to Vue X where we had one root store file and then modules that broke off from that. With PINIA, we create stores that are devoted to each major logical concern of our app and then we can import those stores wherever they're needed. And this is gonna help with bundlers for code splitting, it's gonna give us better type inferences if we're using TypeScript, it's gonna boost our team collaboration and it's gonna add clarity for debugging.

5. Store Organization and Nested Stores

Short description:

When it comes to store organization, it's not always clear how to split up stores based on their concerns. In our example app, we separate the user authentication into one store, while in the restaurants app, we have separate stores for geolocation and restaurants. We can also use nested stores to access data from one store in another. The key takeaway is to group related data into separate stores based on logical concerns.

However, when it comes to actually putting this modularity into practice, it's not always so clear where and how you should be splitting up those stores based on the topic or the domain that they are concerned about. So that brings us to the topic of store organization or as I like to call it, store organization.

Let's take a look at our example app to see a straightforward kind of obvious way that we would separate out our stores and then we're gonna look at one that is a little more complex and nuanced. In the example app, our user can register an account and the user interface is gonna change depending on whether a user is logged in. So if they are, we're gonna see their name and the favorites showing up in the nav bar. So to achieve this authentication behavior, we need to keep track of the user state and also the actions related to that state in terms of registration, login and log out. So it's pretty obvious that this can all be bundled into one store and we might call that the auth or authentication store. But not every store is gonna be that intuitive with how you should be organizing the contents of it.

So let's look at a little less obvious way that we would be store organizing within our Pinia apps. Remember how in our restaurants app, we have these two inputs. The first input takes in the city, the second takes in the search term to find relevant local restaurants that are nearby that city. Both inputs have event listeners that trigger a function when the user types in text. Each function makes a call to the Google Maps API. Specifically the city input is gonna call for the latitude and longitude of the location typed in by the user. And then search is gonna make a call for the relevant restaurants within a certain distance of that city. In other words, both of the city and the search inputs are utilizing geolocation specifically with the Google Maps API. So should we combine this into one store, maybe we would call it Google Maps. Well, when we look at our project holistically, there are quite a few actions that are gonna rely on the Google Maps API. So that would be a lot of code that we're cramming in to one store. And more importantly, the actions aren't all gonna be related to the same kind of logical concerns. So to get clear on how this should all be storeganized, we can break it down to focus more on what is the Google Maps API actually being used for, and what state do we need to be tracking. When we ask those questions, we know that the first input is being used to make geolocation requests, while the second one is dealing with restaurants that match the search term. So yes, those restaurants are nearby, so there's an aspect of location involved with restaurants, but it's not its primary focus, like the other input is that's actually making those geolocation requests. So based on this reasoning, it sounds like we can make two separate stores, each with their shared logical concerns, geolocation and restaurants. But you might be thinking, well, in the restaurant store, when we request that list of relevant restaurants, it requires the use of geolocation data. So this means we need to use the data from the geolocation store inside of the restaurant store. Does that fact just completely ruin our approach here? Well, no, it actually doesn't because that brings us to another point within Pinia, which is called nested stores. With nested stores, we can have a store and then nest another store inside of it based upon what it's parent in this context would have needed. So we're making good progress in understanding some of the Pinia features. So some key takeaways so far to take note of are that we should be grouping related data into their own stores by logical concern.

6. Organizing Stores and Accessing State

Short description:

We can organize our stores by features within our app and cross share state by nesting stores. Pinia allows flexibility in accessing state, with different approaches for option and setup stores. In an option store, state is accessed using 'this' in actions and by passing state to getters. In a setup store, state is accessed directly, like in the setup function. Remember to return state for accessibility outside the store.

We can also organize our stores by features within our app. We don't necessarily want to assume that a store should be created around one API or one library, and we can cross share state amongst and between stores by nesting those stores inside of each other.

Now that we covered all that, let's look at the topic of how do we access our state within Pinia. While other state management libraries can be very prescriptive and really force developers to be accessing and mutating their state in a specific way, this can lead to unnecessary overhead and some rigidity that can feel limiting. But one of the ways that Pinia provides flexibility is by allowing developers to make their own choices about the patterns that they're using. So let's take a look at how we can access state within Pinia and the options we have for doing that.

If we're in a store itself, we have access to the state properties inside of our actions and getters. But there's some considerations to keep in mind as things are a bit different between an options versus a set up store. In an option store, if we're in an action and we need to be accessing state, we can do so using this. Within a getter in an option store, we're going to need to pass in the state to that getter to be able to access it. Now, on the other hand, in a setup store for both actions and getters, we access the state property directly, just as we would within the setup function and the component that's using the composition API. In this example, the state property is a ref, so it's written as city.value. And of course, we have to remember to return everything so it can be accessible outside of the store.

7. Accessing and Mutating Pinia State

Short description:

To access piniaState from inside a component, we can import the store and use the useStore function. Destructuring the properties from the store can make accessing and writing to the state easier. However, we need to be careful to maintain reactivity by using the storeToRefs helper. We can use vModel to bind input values to the pinia state.

Now, what about accessing piniaState from inside of a component? Now, there's going to be a few ways to do this, the most common of which is to import the store into the view component itself and then invoke the useStore function. This is going to allow us to read the state and write to that state using dot notation. However, using dot notation could become burdensome if we're using a lot of different state properties than a component.

So there's a more efficient way that we can actually do this. We're going to make our lives a lot easier by de-structuring the properties from the store so we're not having to write that store name all the time that we're accessing and writing and reading to it. But we do have to be careful about how we do this. Our first instinct might be to do something like this, but this is actually going to be breaking reactivity. In view three, we can't de-structure props unless we use a helper called toRefs. You might be already familiar with this. And in pinia, similarly, we can't de-structure state properties unless we use a pinia helper called storeToRefs, and this will keep the reactivity intact.

Now, we can make use of this de-structured state to push new data into it. Now let's take a look at how vModel would work if we're using that with a pinia store. We can use it to bind something like our input here to the pinia state so that our template stays in sync with the store and reacts accordingly. So those are some common ways to be accessing our pinia state. And some key takeaways that I want you to remember is that in an option store, we can use this. In an option store's getter, we pass in state. In a setup store, we access state properties directly. We don't use this. In a component, we can de-structure state properties with that StoreToRefs helper. And then we can use vModel to bind a value in our component to our store state.

8. Mutating Pinia State

Short description:

We have different ways to mutate the state in Pinia. The most common way is to trigger an action in the store, but we can also change state directly or use the patch method to apply multiple changes at once. Pinia also provides a reset method to reset a store's state. Unfortunately, the reset method is not available with setup stores, but you can write your own setup function to achieve the same functionality. Another useful method is the onAction method, which provides detailed information about actions.

All right, so we looked at some ways to be accessing our pinia state. Now how do we go about mutating that state? As a refresher, when we look at how Vuex worked, when we mutated state, we would dispatch an action, that action would then go ahead and commit a mutation. And this was really the only recommended way to be mutating state unless you maybe were breaking that pattern against those recommended best practices. But of course, pinia is different. We no longer have that mutation step, and we have a lot more flexibility now with how we update our state.

The most common way to be mutating state within pinia is to trigger an action in that store, causing that state to be changed. In this example, clicking the Add to Favorites button triggers the Add to Favorites action in the Favorite Store. Now some people are surprised to learn that this is not the only way to change and mutate state within pinia. We can also change state directly by assigning a new value to the state property. In this example, we have a watcher on the city value, and then if the user deletes the city, we clear out those state properties directly so the user can start a new search.

Another way we can update state is to use the pinia patch method. This allows us to apply multiple changes at the same time to a store state. Here, notice how we're passing in an object with the updated state properties inside of it. Now, using patch comes with some added benefits because it's gonna create a single entry into the dev tools, and if we follow this convention throughout our app, we could easily do a search for patch to locate every place within our application where we're updating state in bulk like this. Another useful thing to note about patch is that it can take an object or a function as its parameter. So for example, if our state property was an array, we could use array methods to be updating our state accordingly.

Conveniently, pinia also offers a reset method. So we can reset a store's entire state to its initial value. In this example, the reset method is used within the store itself to clear out the state of our auth store. Notice how we are in an options store, and so we're using this. We can also use reset in a component like so. Using the reset method like this is super useful when we want to update an entire store at once, like when a user navigates to a certain page. For instance, let's look at this example in the router file where we use pinia's reset method in the router itself. So here we're saying whenever the user navigates back to the homepage, clear out that previous search. Now, unfortunately, the reset method is not available with setup stores, so this is one of the ways where the option store definitely has an advantage over the setup store. And if you're wondering why this is the case, it's because the reset method relies on the state function to create a fresh state, replacing the current store dot state with a new one. But we don't have access to the state function in a setup store. But if you still want that functionality where you reset an entire store, but it's a setup store, so you can't use a reset method, you could write your own setup function that achieves the same thing, where you write an action that resets the entire store, like so. When it comes to mutating our state, Pinea also offers a helpful method that we can use to get very detailed info about our actions. This is called the onAction method.

9. Pinea Features and Learning Resources

Short description:

This method provides hooks to perform logic when an action happens. Pinea is a flexible way to architect and manage state. Explore the Proven Pinea Patterns course for more features and patterns, including plugins and custom behaviors. ViewMastery offers a full playlist of Pinea courses, covering fundamentals, Q&A with the creator, and an exclusive course on 5 Elegant Ways to Use Pinea. The platform also provides content on NUX, composables, utility-first CSS, and more. Team accounts receive a 55% discount, while individuals can choose monthly or annual plans. Grab the free Pinea cheat sheet for fast learning.

And as you can see, this method has a number of hooks we can use to perform some logic when a certain action happens. Notice how we can pass in state as a second argument in case we need it. Here in this example, it's being used to log information about which action has been triggered in the Auth store. To see this working, we can look inside the console, and we indeed see the name of the action, as well as the arguments that were passed into it.

So as you can see, Pinea is an evolved, flexible way we can architect and manage the state in our applications. If you wanna keep learning about the features and patterns within Pinea, I do invite you to take the Proven Pinea Patterns course over at ViewMastery.com. That's going to include everything you just saw in this talk plus more, where we explore plugins and how to extend the functionality of Pinea with your own custom behaviors.

And on our website, we have a full playlist of Pinea courses. So if you or your teammates need to start with the fundamentals, you can build a to-do app with us. We also have a Q&A with the creator of Pinea, Eduardo, so you can learn a bit about common questions that people have and his answers to them. We just released an exclusive course taught by Eduardo called 5 Elegant Ways to Use Pinea. So you can really get inside the mind of the person who wrote the library and use it like a pro.

And of course, being the ultimate learning platform for Vue developers means we have content about topics across the board. So if you're looking for NUX content, you can build a blog with the NUX content module. You can learn about NUX middleware, the NUX server, start to get into more full stack NUX concepts. If you're a fan of the composables from the Vue-Use library, you can take our Coding Better Composables course and learn how to create your own from scratch. You can learn how to use utility-first CSS with the popular Tailwind library, and we have exclusive content taught by the creator of Vue and Vite himself, Evan Yu. All these courses and more can be found in our comprehensive library of content that you can use to become the best Vue developer you possibly can be.

If you're part of a team that's using Vue, I invite you to join together under a team account, and when you do, you can get a 55% discount to access all of our content. Of course, if you're an individual, we have monthly and annual plans to have access to our content. And finally, if you want a free resource that's gonna help you learn Pinea fast, go grab the free Pinea cheat sheet at the URL on your screen.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

React Summit 2022React Summit 2022
20 min
Routing in React 18 and Beyond
Top Content
Concurrent React and Server Components are changing the way we think about routing, rendering, and fetching in web applications. Next.js recently shared part of its vision to help developers adopt these new React features and take advantage of the benefits they unlock.In this talk, we’ll explore the past, present and future of routing in front-end applications and discuss how new features in React and Next.js can help us architect more performant and feature-rich applications.
Vue.js London Live 2021Vue.js London Live 2021
20 min
One Year Into Vue 3
Top Content
Vue 3 may still sound new to many users, but it's actually been released for over a year already. How did Vue 3 evolve during this period? Why did it take so long for the ecosystem to catch up? What did we learn from this process? What's coming next? We will discuss these questions in this talk!
Vue.js London 2023Vue.js London 2023
30 min
Stop Writing Your Routes
The more you keep working on an application, the more complicated its routing becomes, and the easier it is to make a mistake. ""Was the route named users or was it user?"", ""Did it have an id param or was it userId?"". If only TypeScript could tell you what are the possible names and params. If only you didn't have to write a single route anymore and let a plugin do it for you. In this talk we will go through what it took to bring automatically typed routes for Vue Router.

Workshops on related topic

Vue.js London Live 2021Vue.js London Live 2021
169 min
Vue3: Modern Frontend App Development
Top Content
Featured WorkshopFree
The Vue3 has been released in mid-2020. Besides many improvements and optimizations, the main feature of Vue3 brings is the Composition API – a new way to write and reuse reactive code. Let's learn more about how to use Composition API efficiently.

Besides core Vue3 features we'll explain examples of how to use popular libraries with Vue3.

Table of contents:
- Introduction to Vue3
- Composition API
- Core libraries
- Vue3 ecosystem

Prerequisites:
IDE of choice (Inellij or VSC) installed
Nodejs + NPM
JSNation 2022JSNation 2022
141 min
Going on an adventure with Nuxt 3, Motion UI and Azure
WorkshopFree
We love easily created and deployed web applications! So, let’s see what a very current tech stack like Nuxt 3, Motion UI and Azure Static Web Apps can do for us. It could very well be a golden trio in modern day web development. Or it could be a fire pit of bugs and errors. Either way it will be a learning adventure for us all. Nuxt 3 has been released just a few months ago, and we cannot wait any longer to explore its new features like its acceptance of Vue 3 and the Nitro Engine. We add a bit of pizzazz to our application with the Sass library Motion UI, because static design is out, and animations are in again.Our driving power of the stack will be Azure. Azure static web apps are new, close to production and a nifty and quick way for developers to deploy their websites. So of course, we must try this out.With some sprinkled Azure Functions on top, we will explore what web development in 2022 can do.
Vue.js London Live 2021Vue.js London Live 2021
115 min
Building full-stack GraphQL applications with Hasura and Vue 3
WorkshopFree
The frontend ecosystem moves at a breakneck pace. This workshop is intended to equip participants with an understanding of the state of the Vue 3 + GraphQL ecosystem, exploring that ecosystem – hands on, and through the lens of full-stack application development.

Table of contents
- Participants will use Hasura to build out a realtime GraphQL API backed Postgres. Together we'll walk through consuming it from a frontend and making the front-end reactive, subscribed to data changes.
- Additionally, we will look at commonly-used tools in the Vue GraphQL stack (such as Apollo Client and Urql), discuss some lesser-known alternatives, and touch on problems frequently encountered when starting out.
- Multiple patterns for managing stateful data and their tradeoffs will be outlined during the workshop, and a basic implementation for each pattern discussed will be shown.
Workshop level

NOTE: No prior experience with GraphQL is necessary, but may be helpful to aid understanding. The fundamentals will be covered.
React Advanced Conference 2022React Advanced Conference 2022
206 min
Best Practices and Patterns for Managing API Requests and States
Workshop
With the rise of frameworks, such as React, Vue or Angular, the way websites are built changed over the years. Modern applications can be very dynamic and perform multiple API requests to populate a website with fresh content or submit new data to a server. However, this paradigm shift introduced new problems developers need to deal with. When an API request is pending, succeeds, or fails, a user should be presented with meaningful feedback. Other problems can comprise API data caching or syncing the client state with the server. All of these problems require solutions that need to be coded, but these can quickly get out of hand and result in a codebase that is hard to extend and maintain. In this workshop, we will cover how to handle API requests, API states and request cancellation by implementing an API Layer and combining it with React-Query.
Prerequisites: To make the most out of this workshop, you should be familiar with React and Hooks, such as useState, useEffect, etc. If you would like to code along, make sure you have Git, a code editor, Node, and npm installed on your machine.