0
# Store Usage
1
2
Utilities for working with store instances including extracting reactive references, hydration control, and hot module replacement support.
3
4
## Capabilities
5
6
### Store to Refs
7
8
Extracts store properties as reactive refs while maintaining reactivity. This is essential when destructuring stores in Vue components to preserve reactivity.
9
10
```typescript { .api }
11
/**
12
* Creates reactive refs from store properties while maintaining reactivity
13
* @param store - Store instance to extract refs from
14
* @returns Object with reactive refs for all non-function store properties
15
*/
16
function storeToRefs<SS extends StoreGeneric>(store: SS): StoreToRefs<SS>;
17
18
type StoreToRefs<SS extends StoreGeneric> = {
19
[K in keyof SS as SS[K] extends (...args: any[]) => any ? never : K]: SS[K] extends Ref
20
? SS[K]
21
: Ref<SS[K]>;
22
};
23
```
24
25
**Usage Examples:**
26
27
```typescript
28
import { defineStore, storeToRefs } from "pinia";
29
import { computed } from "vue";
30
31
const useCounterStore = defineStore("counter", () => {
32
const count = ref(0);
33
const doubleCount = computed(() => count.value * 2);
34
const increment = () => count.value++;
35
36
return { count, doubleCount, increment };
37
});
38
39
// In a Vue component
40
export default {
41
setup() {
42
const counterStore = useCounterStore();
43
44
// ❌ This breaks reactivity
45
const { count, doubleCount } = counterStore;
46
47
// ✅ This preserves reactivity
48
const { count, doubleCount } = storeToRefs(counterStore);
49
50
// Actions can be destructured directly (they don't need refs)
51
const { increment } = counterStore;
52
53
return {
54
count, // Ref<number>
55
doubleCount, // ComputedRef<number>
56
increment, // Function
57
};
58
}
59
};
60
61
// In a Composition API component
62
<script setup>
63
import { storeToRefs } from "pinia";
64
import { useCounterStore } from "./stores/counter";
65
66
const counterStore = useCounterStore();
67
const { count, doubleCount } = storeToRefs(counterStore);
68
const { increment } = counterStore;
69
70
// count and doubleCount are reactive refs
71
// increment is the original function
72
</script>
73
```
74
75
### Skip Hydration
76
77
Marks an object to skip the hydration process during Server-Side Rendering. Useful for stateful objects that aren't really state.
78
79
```typescript { .api }
80
/**
81
* Tells Pinia to skip the hydration process for a given object
82
* @param obj - Target object to skip hydration
83
* @returns The same object with hydration skip marker
84
*/
85
function skipHydrate<T = any>(obj: T): T;
86
```
87
88
**Usage Examples:**
89
90
```typescript
91
import { defineStore, skipHydrate } from "pinia";
92
import { useRouter } from "vue-router";
93
94
const useAppStore = defineStore("app", () => {
95
const count = ref(0);
96
const router = useRouter();
97
98
// Skip hydration for router instance (not state)
99
const routerInstance = skipHydrate(router);
100
101
// Skip hydration for non-serializable objects
102
const webSocket = skipHydrate(new WebSocket("ws://localhost:8080"));
103
104
return {
105
count, // Will be hydrated
106
routerInstance, // Will skip hydration
107
webSocket, // Will skip hydration
108
};
109
});
110
111
// Useful for plugins, external libraries, or browser-only objects
112
const useServiceStore = defineStore("services", () => {
113
const apiClient = skipHydrate(new ApiClient());
114
const analytics = skipHydrate(window.gtag);
115
116
return { apiClient, analytics };
117
});
118
```
119
120
### Should Hydrate
121
122
Checks whether a value should be hydrated during the SSR hydration process.
123
124
```typescript { .api }
125
/**
126
* Returns whether a value should be hydrated
127
* @param obj - Target variable to check
128
* @returns true if obj should be hydrated, false otherwise
129
*/
130
function shouldHydrate(obj: any): boolean;
131
```
132
133
**Usage Examples:**
134
135
```typescript
136
import { shouldHydrate, skipHydrate } from "pinia";
137
138
const normalObject = { name: "John", age: 30 };
139
const skippedObject = skipHydrate({ socket: new WebSocket("ws://localhost") });
140
141
console.log(shouldHydrate(normalObject)); // true
142
console.log(shouldHydrate(skippedObject)); // false
143
144
// Custom hydration logic
145
function customHydration(state: any, initialState: any) {
146
for (const key in initialState) {
147
if (shouldHydrate(initialState[key])) {
148
state[key] = initialState[key];
149
}
150
}
151
}
152
```
153
154
### Hot Module Replacement
155
156
Enables hot module replacement for stores during development, allowing store updates without losing state.
157
158
```typescript { .api }
159
/**
160
* Creates a function to accept Hot Module Replacement for a store
161
* @param store - Store definition to enable HMR for
162
* @param hot - Hot module object from build tool
163
* @returns Function that accepts new store definitions
164
*/
165
function acceptHMRUpdate(
166
store: StoreDefinition,
167
hot: any
168
): (newStore: StoreDefinition) => any;
169
```
170
171
**Usage Examples:**
172
173
```typescript
174
import { defineStore, acceptHMRUpdate } from "pinia";
175
176
const useCounterStore = defineStore("counter", {
177
state: () => ({ count: 0 }),
178
getters: {
179
doubleCount: (state) => state.count * 2,
180
},
181
actions: {
182
increment() {
183
this.count++;
184
},
185
},
186
});
187
188
// Enable HMR for this store (development only)
189
if (import.meta.hot) {
190
import.meta.hot.accept(acceptHMRUpdate(useCounterStore, import.meta.hot));
191
}
192
193
// For Vite
194
if (import.meta.hot) {
195
import.meta.hot.accept(acceptHMRUpdate(useCounterStore, import.meta.hot));
196
}
197
198
// For Webpack
199
if (module.hot) {
200
module.hot.accept(acceptHMRUpdate(useCounterStore, module.hot));
201
}
202
```
203
204
## Store Instance Interface
205
206
When you call a store definition function, you get a store instance with built-in properties and methods:
207
208
```typescript { .api }
209
interface Store<Id extends string = string, S extends StateTree = {}, G = {}, A = {}> {
210
/** Unique store identifier */
211
$id: Id;
212
213
/** Direct access to store state */
214
$state: UnwrapRef<S>;
215
216
/** Patch state with partial updates */
217
$patch(partialState: _DeepPartial<UnwrapRef<S>>): void;
218
$patch<F extends (state: UnwrapRef<S>) => any>(
219
stateMutator: ReturnType<F> extends Promise<any> ? never : F
220
): void;
221
222
/** Reset state to initial values */
223
$reset(): void;
224
225
/** Subscribe to state changes */
226
$subscribe(
227
callback: SubscriptionCallback<S>,
228
options?: { detached?: boolean } & WatchOptions
229
): () => void;
230
231
/** Subscribe to action calls */
232
$onAction(
233
callback: StoreOnActionListener<Id, S, G, A>,
234
detached?: boolean
235
): () => void;
236
237
/** Dispose of the store and clean up subscriptions */
238
$dispose(): void;
239
}
240
241
type SubscriptionCallback<S> = (
242
mutation: SubscriptionCallbackMutation<S>,
243
state: UnwrapRef<S>
244
) => void;
245
246
type StoreOnActionListener<Id extends string, S, G, A> = (
247
context: StoreOnActionListenerContext<Id, S, G, A>
248
) => void;
249
250
interface StoreOnActionListenerContext<Id extends string, S, G, A> {
251
name: string;
252
store: Store<Id, S, G, A>;
253
args: any[];
254
after: (callback: () => void) => void;
255
onError: (callback: (error: any) => void) => void;
256
}
257
```
258
259
**Usage Examples:**
260
261
```typescript
262
const store = useCounterStore();
263
264
// Access store properties
265
console.log(store.$id); // "counter"
266
console.log(store.$state); // { count: 0 }
267
268
// Patch state
269
store.$patch({ count: 10 });
270
store.$patch((state) => {
271
state.count += 5;
272
});
273
274
// Reset state
275
store.$reset();
276
277
// Subscribe to state changes
278
const unsubscribe = store.$subscribe((mutation, state) => {
279
console.log(`${mutation.type}: ${JSON.stringify(state)}`);
280
});
281
282
// Subscribe to actions
283
const unsubscribeActions = store.$onAction(({ name, args, after, onError }) => {
284
console.log(`Action ${name} called with:`, args);
285
286
after(() => {
287
console.log(`Action ${name} completed`);
288
});
289
290
onError((error) => {
291
console.error(`Action ${name} failed:`, error);
292
});
293
});
294
295
// Clean up
296
unsubscribe();
297
unsubscribeActions();
298
store.$dispose();
299
```
300
301
## Types
302
303
```typescript { .api }
304
type StoreGeneric = Store<string, StateTree, Record<string, any>, Record<string, any>>;
305
306
type _DeepPartial<T> = { [K in keyof T]?: _DeepPartial<T[K]> };
307
308
type SubscriptionCallbackMutation<S> =
309
| SubscriptionCallbackMutationDirect
310
| SubscriptionCallbackMutationPatchObject<S>
311
| SubscriptionCallbackMutationPatchFunction;
312
313
interface SubscriptionCallbackMutationDirect {
314
type: MutationType.direct;
315
storeId: string;
316
events?: DebuggerEvent[] | DebuggerEvent;
317
}
318
319
interface SubscriptionCallbackMutationPatchObject<S> {
320
type: MutationType.patchObject;
321
storeId: string;
322
payload: _DeepPartial<S>;
323
events?: DebuggerEvent[] | DebuggerEvent;
324
}
325
326
interface SubscriptionCallbackMutationPatchFunction {
327
type: MutationType.patchFunction;
328
storeId: string;
329
events?: DebuggerEvent[] | DebuggerEvent;
330
}
331
```