Good Code
The good version gives shared state a store name, derived getter, and action. Components can call loadReviews without knowing every field that changes.
Lesson 08
Keep shared Vue state behind Pinia stores with actions that name writes and async transitions.
import { defineStore } from "pinia";
export const useReviewsStore = defineStore("reviews", {
state: () => ({
reviews: [] as Review[],
isLoading: false,
}),
getters: {
pendingReviews: (state) =>
state.reviews.filter((review) => review.status === "pending"),
},
actions: {
// Actions name the write path and own the async transition.
async loadReviews() {
this.isLoading = true;
try {
this.reviews = await fetch("/api/reviews").then((response) =>
response.json(),
);
} finally {
this.isLoading = false;
}
},
},
});import { reactive } from "vue";
// Any importer can rewrite shared state without a named action.
export const reviewState = reactive({
reviews: [] as Review[],
isLoading: false,
});
export async function loadReviews() {
reviewState.reviews = await fetch("/api/reviews").then((response) =>
response.json(),
);
}The good version gives shared state a store name, derived getter, and action. Components can call loadReviews without knowing every field that changes.
The bad version exports a mutable object and a free function. Any importer can write to state directly, bypassing one reviewable update path.