npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

pinia-di

v3.2.2

Published

DI(dependency-injection) for pinia. work with vue@3

Downloads

13

Readme

pinia-di: Use Pinia more flexibly!

:fire: :fire: A Better way to reuse stores.

DI(dependency-injection) for pinia. work with vue@3.

Installation

npm install pinia pinia-di

Flow Chart

flowchart TD
A{{"StoreProvider[AppStore, MessageStore]\nInjectorA"}} --> B["CompoentApp\nInjectorB\nconst appStore = useStore(AppStore)();\nconst messageStore = useStore(MessageStore)()"]
B --> C{{"StoreProvider[ChildStore]\nInjectorC"}}
B --> D{{"StoreProvider[ChildStore]\nInjectorD"}}
C --> E["ComponentChild\nInjectorE\nconst appStore = useStore(AppStore)();\nconst childStore = useStore(ChildStore)();"]
D --> F["ComponentChild\nInjectorF\nconst appStore = useStore(AppStore)();\nconst childStore = useStore(ChildStore)();"]
E --> G{{"StoreProvider[ChildStore]\nInjectorG"}}
G --> H["ComponentChild\nInjectorH\nconst appStore = useStore(AppStore)();\nconst childStore = useStore(ChildStore)();"]

Core Concepts

  • Injector: Injects and provides stores in the component tree to current component or child components.
  • Store Tree: The store tree is like the component tree, each component get the store from the nearest parent Injector.
  • StoreProvider: A component that uses Injector to provide stores.
  • Store Use: The return type of defineStore.
  • Store: The return type of a Store Use function like useStore();
  • Store Creator: A function that returns a Store Use.
  • InjectionContext: The parameter that the Store Creator will receive.

Define Store Creator

A Store Creator is a creator function that returns a defineStore() result.

For example: the AppStore is a Store Creator, and the return of AppStore() is Store Use:

import { defineStore } from 'pinia';
import { InjectionContext } from 'pinia-di';

// Store Creator
export const AppStore = (ctx: InjectionContext) => {
  return defineStore(ctx.useStoreId('App'), {
    //...
  });
}

// Store Use
const useAppStore = AppStore();

// Store
const appStore = useAppStore();

InjectionContext: { getStore, useStoreId, onUnmounted }

getStore: Get another store that has been provided by the current injector or parent injector.

import { InjectionContext } from 'pinia-di';
import { OtherStore } from './stores/other';

export const AppStore = ({ getStore }: InjectionContext) => {
  return defineStore('app', () => {
    const state = reactive({});
    const test = () => {
      // the OtherStore must be provided by `current injector` or  `parent injector`
        const otherStore = getStore(OtherStore);
        console.log(otherStore.xx);
    };
    
    return {
      state,
      test
    }
  });
}

useStoreId: Because pinia uses id to identify one store, but our Store Creator is reusable, you can use the method useStoreId to generate the unique id.

import { InjectionContext } from 'pinia-di';
export const TestStore = ({ useStoreId }: InjectionContext) => {
  // store id will be `test-1`, `test-2`, ...
  return defineStore(useStoreId('test'), () => {
    const state = reactive({});
    
    return {
      state
    }
  });
}

onUnmounted: Bind a function that will be invoked when the store is unmounted.

import { InjectionContext } from 'pinia-di';
export const TestStore = ({ onUnmounted }: InjectionContext) => {
  const useTestStore = defineStore(useStoreId('test'), () => {
    const state = reactive({});
    const dispose = async () => {
      console.log('dispose');
    };

    const remove = onUnmounted(dispose);
    // unbind the callback
    // remove()

    return {
      state
    }
  });

  return useTestStore;
}

Provide Stores

You can use composition api useProvideStores or component StoreProvider to provide stores.

useProvideStores

<script lang="ts" setup>
import { createApp } from 'vue';
import { useProvideStores, useStore } from 'pinia-di';
import { TestStore } from '@/stores/testStore';

// the testStore is provided by parent injector
const testStore = useStore(TestStore);

// 'test' is the injector name that helps to debug
useProvideStores([TestStore], 'test');

// testStoreNew is provided by the `useProvideStores` above
const testStoreNew = useStore(TestStore);
</script>

StoreProvider

Use StoreProvider to provide stores.

:fire: Tip: Because the stores prop is only used once, if it changes after the component is mounted, the new store prop will be ignored.

:fire: If you want to conditionally provide different stores, you need to write different components to provide each self.

App.vue

<script setup>
import { StoreProvider } from 'pinia-di';
import { AppStore } from '@/stores/appStore';
import { TestStore } from '@/stores/testStore';
</script>

<template>
  <!-- // 'app' is the injector name that help to debug -->
  <StoreProvider :stores="[AppStore]" name="app">
    <Main />

    <div>
      <StoreProvider :stores="[TestStore]">
        <div>test a</div>
      </StoreProvider>
      
      <StoreProvider :stores="[TestStore]">
        <div>test n</div>
      </StoreProvider>
    </div>
  </StoreProvider>
</template>

And, you can provide stores in the app.provide for the whole vue app.

pinia-di provides a helper function getProvideArgs to do this.

import { createApp } from 'vue';
import { getProvideArgs } from 'pinia-di';
import { AppStore } from '@/stores/appStore';

const app = createApp();
// 'app' is the injector name that help to debug
app.provide(...getProvideArgs([AppStore], 'app'));

app.mount('#app');

Using Store

Component.vue

<script setup>
import { useStore } from 'pinia-di';
import { AppStore } from '@/stores/appStore';

const appStore = useStore(AppStore);
</script>

Store Out Of Component: Singleton Store

*** Tips: If you use a Singleton Store, you can't get InjectionContext when then store is created ***

stores/messageStore.ts

import { defineStore } from 'pinia';

export const MessageStore = (/* no `ctx: InjectionContext` */) => {
  return defineStore('message'), {
    state: {}
  });
}

export const useMessageStore = MessageStore();

Then, if you want to use the same store of messageStore for MessageStore, you will use the use flag when you provide stores.

App.vue

<script setup>
import { StoreProvider, useStore } from 'pinia-di';
import { AppStore } from '@/stores/appStore';
import { useMessageStore, MessageStore } from '@/stores/messageStore';

const stores = [
  AppStore, { creator: MessageStore, use: useMessageStore }
]
</script>

<template>
  <StoreProvider :stores="stores">
    <Main />
  </StoreProvider>
</template>

When the child components get the store from useStore(MessageStore), they will get the same store returned by useMessageStore() that was created before, not create new messageStore.

Component.vue

<script setup>
import { useStore } from 'pinia-di';
import { MessageStore } from '@/stores/messageStore';

// messageStore === useMessageStore(): true
const messageStore = useStore(MessageStore);
</script>

Get Other Stores In One Store

stores/userStore.ts

import { defineStore } from 'pinia';
import { useStoreId } from 'pinia-di';

export const UserStore = ({ getStore, useStoreId }: InjectionContext) => {
  return defineStore(useStoreId('user'), () => {
    const state = reactive({});
    const test = () => {
      // get another store that the parent component or self provided
        const appStore = getStore(AppStore);
        console.log(appStore.xxx);
    };
    
    return {
      state,
      test
    }
  });
}

Store OnUnmounted

stores/appStore.ts

import { defineStore } from 'pinia';

export const AppStore = ({ onUnmounted, useStoreId }: InjectionContext) => {
  // define store, useStoreId('main') generate the unique id per `Store Instance`
  return defineStore(useStoreId('main'), () => {
    const state = reactive({});
    const dispose = async () => {
      // console.log('dispose')
    };

    onUnmounted(dispose);

    return {
      state
    };
  });
}

Store Tree

If the same store creator is provided by more than one parent, the useStore will get the nearest one.

ParentA.Vue

<template>
  <ParentB/>
</template>

<script setup>
import { StoreProvider } from 'pinia-di';
import { TestStore } from '@/stores/testStore';

const stores = [TestStore];
</script>

<template>
  <StoreProvider :stores="stores">
    <ParentB />
  </StoreProvider>
</template>

ParentB.Vue

<template>
  <Child/>
</template>

<script setup>
import { provideStores } from 'pinia-di';
import { TestStore } from '@/stores/testStore';

const stores = [TestStore];
</script>

<template>
  <StoreProvider :stores="stores">
    <Child />
  </StoreProvider>
</template>

Child.Vue

<script setup>
import { useStore } from 'pinia-di';
import { TestStore } from '@/stores/testStore';

// will get the store provided by ParentB
const testStore = useStore(TestStore);
</script>

disposeOnUnmounted

pinia-di will call store.$dispose() when the injected component is unmounted.

If do not want this, you can use disposeOnUnmounted to disable it.

<script setup>
import { provideStores } from 'pinia-di';
import { TestStore } from '@/stores/testStore';

const stores = [
  { creator: TestStore, disposeOnUnmounted: false }
];
</script>