or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdoptions-api.mdpinia-instance.mdstore-definition.mdstore-usage.md

store-usage.mddocs/

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

```