Files
stack/v1/packages/mosaic/framework/skills/pinia/references/features-plugins.md
T

3.6 KiB

name, description
name description
plugins Extend stores with custom properties, methods, and behavior

Plugins

Plugins extend all stores with custom properties, methods, or behavior.

Basic Plugin

import { createPinia } from 'pinia';

function SecretPiniaPlugin() {
  return { secret: 'the cake is a lie' };
}

const pinia = createPinia();
pinia.use(SecretPiniaPlugin);

// In any store
const store = useStore();
store.secret; // 'the cake is a lie'

Plugin Context

Plugins receive a context object:

import { PiniaPluginContext } from 'pinia';

export function myPiniaPlugin(context: PiniaPluginContext) {
  context.pinia; // pinia instance
  context.app; // Vue app instance
  context.store; // store being augmented
  context.options; // store definition options
}

Adding Properties

Return an object to add properties (tracked in devtools):

pinia.use(() => ({ hello: 'world' }));

Or set directly on store:

pinia.use(({ store }) => {
  store.hello = 'world';
  // For devtools visibility in dev mode
  if (process.env.NODE_ENV === 'development') {
    store._customProperties.add('hello');
  }
});

Adding State

Add to both store and store.$state for SSR/devtools:

import { toRef, ref } from 'vue';

pinia.use(({ store }) => {
  if (!store.$state.hasOwnProperty('hasError')) {
    const hasError = ref(false);
    store.$state.hasError = hasError;
  }
  store.hasError = toRef(store.$state, 'hasError');
});

Adding External Properties

Wrap non-reactive objects with markRaw():

import { markRaw } from 'vue';
import { router } from './router';

pinia.use(({ store }) => {
  store.router = markRaw(router);
});

Custom Store Options

Define custom options consumed by plugins:

// Store definition
defineStore('search', {
  actions: {
    searchContacts() {
      /* ... */
    },
  },
  debounce: {
    searchContacts: 300,
  },
});

// Plugin reads custom option
import debounce from 'lodash/debounce';

pinia.use(({ options, store }) => {
  if (options.debounce) {
    return Object.keys(options.debounce).reduce((acc, action) => {
      acc[action] = debounce(store[action], options.debounce[action]);
      return acc;
    }, {});
  }
});

For Setup Stores, pass options as third argument:

defineStore(
  'search',
  () => {
    /* ... */
  },
  {
    debounce: { searchContacts: 300 },
  },
);

TypeScript Augmentation

Custom Properties

import 'pinia';
import type { Router } from 'vue-router';

declare module 'pinia' {
  export interface PiniaCustomProperties {
    router: Router;
    hello: string;
  }
}

Custom State

declare module 'pinia' {
  export interface PiniaCustomStateProperties<S> {
    hasError: boolean;
  }
}

Custom Options

declare module 'pinia' {
  export interface DefineStoreOptionsBase<S, Store> {
    debounce?: Partial<Record<keyof StoreActions<Store>, number>>;
  }
}

Subscribe in Plugins

pinia.use(({ store }) => {
  store.$subscribe(() => {
    // React to state changes
  });
  store.$onAction(() => {
    // React to actions
  });
});

Nuxt Plugin

Create a Nuxt plugin to add Pinia plugins:

// plugins/myPiniaPlugin.ts
import { PiniaPluginContext } from 'pinia';

function MyPiniaPlugin({ store }: PiniaPluginContext) {
  store.$subscribe((mutation) => {
    console.log(`[🍍 ${mutation.storeId}]: ${mutation.type}`);
  });
  return { creationTime: new Date() };
}

export default defineNuxtPlugin(({ $pinia }) => {
  $pinia.use(MyPiniaPlugin);
});