SyntaxStudy
Sign Up
Vue.js Async Actions and Store Composition
Vue.js Beginner 1 min read

Async Actions and Store Composition

Pinia actions are regular async functions — simply mark them `async` and use `await` inside. There is no special handling for loading or error state, which means you manage those yourself with additional state properties. A common pattern is an `isLoading` ref and an `error` ref that the action updates as the async operation proceeds. Stores can be composed by importing one store's instance inside another. For example, an `orders` store might import the `auth` store to attach the current user's ID to every request. This explicit import graph keeps dependencies visible and avoids the circular reference issues that plagued Vuex modules. For cross-cutting concerns like logging, persistence, or error tracking, Pinia supports plugins registered via `pinia.use(plugin)`. A plugin is a function that receives the store context on every `defineStore` call and can add properties, wrap actions, or subscribe to mutations. The built-in `pinia-plugin-persistedstate` package, for instance, serialises selected store state to `localStorage` with a single line of configuration.
Example
// src/stores/posts.ts
import { defineStore } from 'pinia';
import { ref }         from 'vue';
import { useAuthStore } from './auth';

export const usePostsStore = defineStore('posts', () => {
  const posts     = ref([]);
  const isLoading = ref(false);
  const error     = ref(null);

  // Compose another store
  const auth = useAuthStore();

  async function fetchPosts() {
    isLoading.value = true;
    error.value     = null;
    try {
      const res    = await fetch('/api/posts', {
        headers: { Authorization: `Bearer ${auth.token}` }
      });
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      posts.value = await res.json();
    } catch (e) {
      error.value = e.message;
    } finally {
      isLoading.value = false;
    }
  }

  async function createPost(payload) {
    const res  = await fetch('/api/posts', {
      method:  'POST',
      headers: { 'Content-Type': 'application/json',
                 Authorization: `Bearer ${auth.token}` },
      body: JSON.stringify(payload),
    });
    const post = await res.json();
    posts.value.unshift(post);
    return post;
  }

  return { posts, isLoading, error, fetchPosts, createPost };
});