Writing in the front

Pinia. Js is a next-generation state manager developed by key members of the Vue.js team, redesigned using the Composition Api and considered the next generation of Vuex. Pinia is a Vue state management library that allows for global sharing of state across components and pages, and is an imminent replacement for Vuex due to its simplicity of design and support for typescript.

Maybe you’re thinking that in Vue3 the Composition API can use reactive variables to globally share state, so why Pinia? The point you are missing is that it is perfectly fine to do this on a single page, but if the page is rendered on the server, there will be a security hole.

The advantages of Pinia are:

  • Full Typescript support and no complex configuration is required
  • Mutations have been removed, eliminating the need to distinguish between synchronous and asynchronous methods and allowing actions to support both synchronous and asynchronous methods, thus forming a three-sanner pattern of state, getters and actions
  • Support for server-side rendering
  • There is no need for nesting dolls 🪆, there is no module nesting, only a simple store concept that can be used freely, forming a better code splitting experience
  • Compared to Vuex’s more lightweight design, compact code packaging volume

All in all, Pinia is not only cute, but also simple, lightweight and easy to use.

Begin to use

Install Pinia

$ yarn add pinia
$ npm i pinia
$ pnpm i pinia
Copy the code

Create the Store

According to the format suggestions given by Pinia, we can create a store directory under the SRC directory, create an index.ts file under it as the global configuration file of store, and export store for global mounting.

//src/store/index.ts
import {App} from "vue"
import {createPinia} from "pinia"

const store = createPinia()

export function setupStore(app:App<Element>){
  app.use(store)
}

export {store}
Copy the code

Mount the store file globally in main.ts:

//src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
import {setupStore} from "@stores/index"

const app = createApp(App);
/ / mount store
setupStore(app)
app.mount("#app")
Copy the code

A simple example

Start by defining a store status management file for counter.

//src/store/modules/counter.ts
import {defineStore} from "pinia"

export const useCounterStore = defineStore("counter", {state:() = >{
    return {
      count:0}},actions: {increment(){
      this.count++
    }
  }
})
Copy the code

Then use it in a vue file:

<template>
  <div>count:{{counter.count}}</div>
</template>
<script lang="ts" setup>
import { useCounterStore } from '@store/modules/counter';

const counter = useCounterStore();
counter.count++
counter.$patch({count:counter.count})
counter.increment()
</script>
Copy the code

State Data status

Define state

Before using the warehouse status data, you need to define a warehouse using defineStore() and name it with a unique name.

//src/store/modules/counter.ts
import {defineStore} from "pinia"

export const useCounterStore = defineStore("counter", {state:() = >{
    return {
      count:0}}})Copy the code

Using state

In Pinia, state is defined using functions that return initialization data, allowing Pinia to be used both as a server and as a client.

We have defined a store repository and use useStore() to get the warehouse state data.

<template> <div>count:{{count}}</div> <div>doubleCount:{{doubleCount}}</div> </template> <script lang="ts" setup> import  { useCounterStore } from '@store/modules/counter'; const counter = useCounterStore(); // Use computed to obtain responsive data const doubleCount = computed(()=>counter. Count *2); const {count} = counter; </script>Copy the code

Note that when importing store uses state, direct deconstruction makes the variable unresponsive. In the above data, the count variable is unresponsive, and its value is constant. To do this, you should export storeToRefs from Pinia to get reactive data in the same way that the Composition API uses props.

const counter = useCounterStore();
const {count} = storeToRefs(counter)
Copy the code

For this, count will now be responsive, which will make the state variable in the Store responsive, and storeToRefs will automatically skip all actions and properties that don’t need to be responsive.

Changing state status

In general, you can retrieve and modify state data directly from the store instance. You can use store.$reset() to reset the state data back to its original value.

import { useCounterStore } from '@stores/modules/counter';
const counter = useCounterStore();
counter.count++;
Copy the code

Of course, you can also update by using Pinia’s built-in API, $patch(), which allows you to make multiple data changes at the same time. $patch() allows you to combine multiple changed values, which can be added, deleted, or checked like an array.

import { useTodosStore } from "@stores/modules/todos";
const todosStore = useTodosStore()
todosStore.$patch(state= >{
  state.todos.push({
    name:"yichuan".age:18
  })
  state.isTrue = true
})
Copy the code

$patch() allows you to combine multiple changed values, which can be added, deleted, or checked like an array.

To replace the value of state, you can replace the value of state in store by setting the value of $state.

store.$state = {counter:Awesome!.name:"onechuan"}
Copy the code

It is also possible to change the value of the entire state through an instance of Pinia.

pinia.state.value= {}
Copy the code

However, it is generally not recommended to modify the value of state directly. Pinia recommends using the Actions method to modify and manage the value of state.

Listen for state state changes

Subscribe to the value of state: You can observe changes in state through the store’s $subscribe() method, which is similar to the subscribe method. The advantage of using $subscribe() over regular Watch () is that the subscription is triggered only once after the patch is released.

numerStore.$subscribe((mutation,state) = >{
  mutation.counter
  mutation.name
  mutation.isAdmin
  localStorage.setItem("numer".JSON.stringify(state))
})
Copy the code

By default, status subscriptions are bound to the component to which they were added (if stored in the component’s Setup ()). This means that the component is automatically removed when it is uninstalled. Detached :true If you want to keep components after they are removed, you can separate the state subscription from the current component by setting {detached:true} as the second parameter.

const someStore = useSomeStore()
someStore.$subscribe(callback, { detached: true })
Copy the code

Getters

Getters have exactly the same computed value as store state and can be defined by the getters property in defineStore(), which accepts state as the first argument to the arrow function.

export const useStore = defineStore('counter', {
  state: () = > ({
    counter: 0,}).getters: {
    doubleCount: (state) = > state.counter * 2,}})Copy the code

For the most part, getters will rely only on state; however, they may need to use other getters. Therefore, when we define a normal function, we can access the entire store instance through this function, but we need to define the type of the return type. Due to known limitations in TS, getters using arrow function definitions and not using this are not affected.

import {defineStore} from "pinia"

export const useNumerStore = defineStore("numer", {state:() = >({
    counter:0.name:"numer".isAdmin:true
  }),
  getters: {doubleCount(state){
      return state.counter * 2
    },
    // When using this, the type of return value must be set exactly
    doublePlusOne():number{
      return this.counter * 2 + 1}}})Copy the code

Of course, you can get multiple getters by evaluating properties, and you need to get any other getters by using this.

export const useStore = defineStore("main", {state:() = >({
    counter:0
  }),
  getters: {doubleCount:state= >state.counter * 2.doubleCountPlusOne(){
      return this.doubleCount + 1}}})Copy the code

A getter is just a behind-the-scenes calculation of a property, so no parameters can be passed to it. But you can return a function from the getter to take any argument.

export const useStore = defineStore('main', {
  getters: {
    getUserById: (state) = > {
      return (userId) = > state.users.find((user) = > user.id === userId)
    },
  },
})
Copy the code

Use in components:

<div> user: {{getUserById(1)}}</div>

const numerStore = useNumerStore()
const {getUserById} = numerStore
Copy the code

Note that when you do this, getters are no longer cached; they are just the functions you call. However, you can cache some results in the getter itself, which is not common but should prove more efficient.

export const useStore = defineStore('main', {
  getters: {
    getActiveUserById(state) {
      const activeUsers = state.users.filter((user) = > user.active)
      return (userId) = > activeUsers.find((user) = > user.id === userId)
    },
  },
})
Copy the code

To get getters from other stores, use another store getter. You can use it directly in the getter, not much different from using it in a vue file.

import { useOtherStore } from './other-store'

export const useStore = defineStore('main', {
  state: () = > ({
    // ...
  }),
  getters: {
    otherGetter(state) {
      const otherStore = useOtherStore()
      return state.localData + otherStore.data
    },
  },
})
Copy the code

Actions

Actions are equivalent to methods in components that define actions attributes in defineStore() and perfectly define business logic.

export const useStore = defineStore('main', {
  state: () = > ({
    counter: 0,}).actions: {
    increment() {
      this.counter++
    },
    randomizeCounter() {
      this.counter = Math.round(100 * Math.random())
    },
  },
})
Copy the code

In the code above, we can see that actions are somewhat similar to Getters, but in fact they are different.

  • Similarities: Actions and getters both have full type support to access the entire Store instance.

  • Difference: Actions can be asynchronous, waiting for any API call or even other actions.

Note that it doesn’t matter which library you use as long as you get a Promise, you can even use the local FETCH function (browser only):

import { mande } from 'mande'

const api = mande('/api/users')

export const useUsers = defineStore('users', {
  state: () = > ({
    userData: null.// ...
  }),

  actions: {
    async registerUser(login, password) {
      try {
        this.userData = await api.post({ login, password })
        showTooltip(`Welcome back The ${this.userData.name}! `)}catch (error) {
        showTooltip(error)
        // let the form component display the error
        return error
      }
    },
  },
})
Copy the code

Similarly, actions can be used interchangeably with state and getters, and actions can be accessed directly through this.

// src/store/user.ts
export const useUserStore = defineStore({
  "user".state: () = > ({
    userData: null
  }),
  actions: {async login(account, pwd) {
      const { data } = await api.login(account, pwd)
      this.setData(data) // Call another action method
      return data
    },
    setData(data) {
      this.userData = data
    }
  }
})
Copy the code

You can also call an action from an action in another store and access its internal methods by importing the corresponding store.

// src/store/user.ts

import { useAppStore } from './app'
export const useUserStore = defineStore({
  id: 'user'.actions: {
    async login(account, pwd) {
      const { data } = await api.login(account, pwd)
      const appStore = useAppStore()
      appStore.setData(data) // Call the app Store action method
      return data
    }
  }
})
Copy the code
// src/store/app.ts
export const useAppStore = defineStore({
  "app".state:() = >{
    userData: null
  },
  actions: {
    setData(data) {
      this.userData = data
    }
  }
})
Copy the code

Refer to the article

  • Pinia Official Document
  • The next generation of status Management tools, Pinia. Js Guide

Write in the last

This article is learned and summarized by reading documents and related articles. It is an experiment before application and production in the company. It should be a piece of work for competent executives. Pinia is very good state management, simple and lightweight and easy to use. In the summary of the article, also made some code practice, but there will be some loopholes, but also hope you correct.