In Pinia, how does an order store's placeOrder action use the auth store's token, and why call useAuthStore() before the first await?
answer
- call the other useStore in the action
- top of setup for setup stores
- each action activates its pinia
- await lets other code run
- one pinia per server request
basics
~20 sCall useAuthStore() inside the action, or at the top of a setup store, then read auth.token or call its actions. Do it before any await: after one, another request's pinia may be the active one.
solid answer
~40 sUsing another store from an action is just calling its `useStore()`: `const auth = useAuthStore()` inside `placeOrder` in an option store, or once at the top of a setup store's setup function. Then use it directly: `if (!auth.isAuthenticated) throw …`, `await auth.refreshToken()`, `api.placeOrder(auth.token, this.items)`. The placement rule comes from how `useAuthStore()` finds its pinia: outside a component it uses the **active** pinia, and Pinia makes the order store's pinia active at the start of every action. After an `await`, other code has run; on a server rendering several requests at once, each with its own pinia, the active one may now belong to another request, so a `useAuthStore()` there can return another user's auth store. Pinia's docs therefore say to make every `useStore()` call before the first `await`.
code
ts · 19 linesimport { defineStore } from 'pinia'
import { useAuthStore } from './auth'
import { api } from '@/api'
export const useOrderStore = defineStore('order', {
state: () => ({ items: [] as { sku: string; qty: number }[], lastOrderId: '' }),
actions: {
async placeOrder() {
const auth = useAuthStore() // before any await
if (!auth.isAuthenticated) throw new Error('Sign in to check out')
await auth.refreshToken()
const { id } = await api.placeOrder(auth.token, this.items)
// do NOT call useAuthStore() down here
this.lastOrderId = id
return id
},
},
})go deeper
Know that an action uses another store by calling its useStore() function and then reading or calling it directly.
Explain where to call useAuthStore() in option and setup stores, and that Pinia activates the store's pinia before each action.
Explain the active-pinia race across await under server rendering, and enforce lookups before the first await in review.
Define how cross-store workflows like checkout are structured so dependencies run one way and stay safe under server rendering.
## Using another store inside an action Stores compose by calling each other's `useStore()` functions; there is no registration or injection step. | Store style | Where to call `useAuthStore()` | Then | |---|---|---| | Option store | at the top of the action body | `auth.token`, `auth.refreshToken()` | | Setup store | once, at the top of the setup function | use `auth` inside any action | The docs' own example is an action that reads `auth.isAuthenticated` and throws when the user is not signed in. The order store's checkout does the same, then passes the token to the API. ## How useAuthStore() finds a pinia `useAuthStore()` must return the auth store **of the right pinia**. It decides like this: 1. If a pinia is passed explicitly, use it. 2. Otherwise, if there is an injection context (component setup, or code running inside the app's context), inject the app's pinia. 3. Otherwise, use the **active pinia**, a module-level variable. An action called from a click handler runs outside any injection context, so step 3 applies. Pinia covers that case: the wrapper around every action calls `setActivePinia()` with the store's own pinia before the action body runs. At the top of `placeOrder`, the active pinia is guaranteed to be the order store's. ## Why before the first await An `await` suspends the action and lets any other code run until the promise settles: 1. `placeOrder` starts; the order store's pinia is active. 2. It awaits `api.placeOrder(…)`. 3. Meanwhile, other work runs. On a server that renders several requests concurrently, each request creates its own pinia and makes it active. 4. `placeOrder` resumes. The active pinia may now be another request's. 5. A `useAuthStore()` call at this point resolves in that pinia and returns **another user's** auth store. In a browser there is normally a single pinia, so the bug is invisible there and appears only under server rendering, which makes it expensive to find. The documented rule removes it: call every `useStore()` at the top of the action, before any `await`, and keep the references. ## A checkout action that follows the rule - `const auth = useAuthStore()` first line; - guard: `if (!auth.isAuthenticated) throw new Error('Sign in to check out')`; - `await auth.refreshToken()` if the token may be stale (an action of the other store is fine to call and await); - `await api.placeOrder(auth.token, this.items)`; - write the result to the order store. The reference `auth` stays valid after the awaits; only a *new* lookup is at risk. ## Calls in both directions The order store may call auth's actions and auth's `logout()` may call the order store's `clear()`: actions run after both stores exist, so mutual action calls are fine. The restriction for stores that use each other is narrower and concerns reading each other's state directly while their setup functions are still running. ## Slips to avoid - Calling `useAuthStore()` at module top level in the store file: there may be no active pinia yet, and it is shared by every request. - Copying `auth.token` into order state: it becomes a stale snapshot. - Looking up a store after an `await` "to get the freshest data": the reference already gives fresh data; the late lookup only risks the wrong pinia. ## Setup stores make the rule automatic A setup store's function runs once, when the store is created, and Pinia runs it for the store's own pinia. Calling `const auth = useAuthStore()` at the top of that function therefore resolves correctly, and every action simply closes over `auth`: - no action ever looks the store up again, so no lookup can land after an `await`; - the dependency is visible at the top of the file, like an import; - the same reference serves actions and `computed` getters alike. That makes setup stores convenient for stores with several dependencies in an app that renders on the server. In an option store, the discipline has to be kept by hand in every async action, and a review checklist item ("all `useXStore()` calls before the first `await`") is the usual guard. Pinia's cookbook shows exactly this pattern, with the correct call at the top and a marked-wrong call after an `await`.
- Where does a Pinia setup store call useAuthStore() so its actions can use it?Once, at the top of the setup function: `const auth = useAuthStore()`. Pinia runs the setup while creating the store for its own pinia, so the lookup resolves correctly, and every action then closes over `auth`. The actions never need to look the store up again, so the await rule is satisfied automatically.
- Why is holding the auth store reference across an await safe when a new lookup is not?The reference points at one specific store object, bound to one pinia; awaiting does not change it, and reading `auth.token` later still returns that store's live value. A new `useAuthStore()` call re-resolves the pinia from whatever is active at that moment, which after an await may be another request's.
saying these in an interview costs you the question
- Another store must be injected into an action, not called with useStore().
- Calling useAuthStore() after the await gives fresher data.
- The active pinia cannot change while an action is awaiting.
- Calling useAuthStore() once at module top level is the safest option.
- Two stores can never call each other's actions.