Search code examples
vue.jsnuxt.jsstatestorepinia

Extending or composing Pinia setup stores for reusable getters, setters and actions


Imagine we have a Pinia setup store that implements a basic set of state, getters and actions:

export const useBaseStore = defineStore('base-store', () => {
  const name = ref<string>('');
  const age = ref<number>(1);
  const ageString = computed(() => `${name.value} is aged ${size.value}`)

  function doubleAge() {
    age.value *= 2;
  }

  return { name, age, ageString, doubleAge }
}

How can we extend or compose this store into further stores to reuse this set of state behaviours? For example

export const usePersonStore = defineStore('person-store', () => {

  // ... somehow compose all of 'base-store'

  function tripleAge() {
    age.value *= 3;
  }

  return {
    // ...include all getters, setters and actions returned by 'base-store', as well as those defined here
    tripleAge
  }
}

export const useOtherStore = defineStore('other-store', () => {

  // ... somehow compose all of 'base-store'

  function sayHello() {
    console.log('Hello')
  }

  return {
    // ...include all getters, setters and actions returned by 'base-store', as well as those defined here
    sayHello
  }
}

Solution

  • Composing Pinia can be done a few ways. Rather than trying to extend a store, I created a composable out of your base store, and then defined two stores from that.

    The person-store needs access to age from within the tripleAge() function, so the setup is bringing in the base as a constant.

    The other-store just directly uses a spread operator because it only adds one new function, and doesn't need access to the other properties/functions.

    import { ref, computed } from 'vue';
    import { defineStore } from 'pinia';
    
    const useBaseStore = () => {
      const name = ref<string>('');
      const age = ref<number>(1);
      const ageString = computed(() => `${name.value} is aged ${age.value}`);
    
      function doubleAge() {
        age.value *= 2;
      }
    
      return { name, age, ageString, doubleAge };
    };
    
    export const usePersonStore = defineStore('person-store', () => {
      const base = useBaseStore();
      function tripleAge() {
        base.age.value *= 3;
      }
      return { ...base, tripleAge };
    });
    
    export const useOtherStore = defineStore('other-store', () => {
      function sayHello() {
        console.log('Hello');
      }
      return {
        ...useBaseStore(),
        sayHello,
      };
    });