Search code examples
vue.jspinia

Vue 3 How do I properly use a pinia store in another pinia store?


I have a todosStore pinia store that performs actions (CRUD operations) against the backend. In this todosStore, I need to use another pinia store bannerStore for updating the bootstrap alert component's message and class in the template.

If I use the bannerStore in the todosStore.js like below, I get the following error Uncaught Error: [🍍]: "getActivePinia()" was called but there was no active Pinia. Are you trying to use a store before calling "app.use(pinia)"?

import { useBanner } from "./bannerStore";

const bannerStore = useBanner()

export const useTodos = defineStore("todos", {
    state: () => ({
        ...
    }),
    actions: {
            },
})

If I use the store inside each action like below, then it works properly.

export const useTodos = defineStore("todos", {
    state: () => ({
        todos: [],
    }),
    actions: {
        async fetchAllTodos() {
            try {
                const res = await todoService.getAllTodos()
                if (res.status === 200) {
                    this.todos = res.data
                }
            } catch (error) {
                const bannerStore = useBanner()
                bannerStore.setBanner(`Failed to retrieve all todos: ${error}`, 'error')
            }
        },
        async addTodo(todo) {
            const bannerStore = useBanner()
            try {
                const res = await todoService.addTodo(todo)
                if (res.status === 201) {
                    this.todos.push(todo)
                    bannerStore.setBanner("Todo added successfully!", 'success')
                }
            } catch (error) {
                bannerStore.setBanner(`Failed To add todo item ${todo}: ${error}`, 'error')

            }
        },
        async deleteTodoById(todoId) {
            const bannerStore = useBanner()
            try {
                const res = await todoService.deleteTodo(todoId)
                if (res.status === 200) {
                    this.todos = this.todos.filter(t => t.id !== todoId)
                    bannerStore.setBanner(`Successfully deleted todo with Id ${todoId}`, 'success')
                }
            } catch (error) {
                bannerStore.setBanner(`Failed operation: deleteTodoById() for id ${todoId}: ${error}`, 'error')
            }
        }
    },

I would like to know if what I am currently doing (initializing bannerStore in each action) is the optimal way of doing it, or if there is a better way?


Solution

  • The 2nd method is correct. Even if you call useStore a million times, it will only do one thing: return the initialized store. You may not use useStore at the startup thread because of pinia not yet attached to the component.

    If you're worried about it use the nice composition API syntax

    export const useTodos = defineStore("todos", () => {
        const todos = reactive([])
      const banner = useBannerStore()
        
    
      function addTodo() {
    
      }
    
      return { todos, addTodo }
    })