Skip to content

Commit 6a06b27

Browse files
api and test improvements
1 parent 2bbe297 commit 6a06b27

File tree

7 files changed

+191
-119
lines changed

7 files changed

+191
-119
lines changed

README.md

Lines changed: 150 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,100 @@
11
# Vue Cache Store
22
Dynamically create, re-use, and destroy [Pinia](https://pinia.vuejs.org/) like stores.
33

4-
## Use Case
5-
When you need reusable non-trivial computed/reactive objects in multiple components.
6-
74
## Installation
85

96
`$ npm i vue-cache-store`
107

8+
## Primary Use Case
9+
When using non-trivial derived reactive objects in multiple components.
10+
11+
```ts
12+
// person-data.ts
13+
type Person = {
14+
id: number,
15+
name: string,
16+
}
17+
18+
export const people = ref<Person[]>([{
19+
id: 99,
20+
firstName: 'Jim',
21+
lastName: 'Kirk'
22+
}])
23+
export const getPerson = (id: number) => people.value.find(person => person.id === id)
24+
25+
export const getPersonInfo = (person: Person) => {
26+
const firstName = computed(() => person.firstName)
27+
const lastName = computed(() => person.lastName)
28+
// 🧠 imagine this is non-trivial and complicated 🧠
29+
return {
30+
id: computed(() => id),
31+
firstName,
32+
lastName,
33+
fullName: computed(() => firstName.value + ' ' + lastName.value)
34+
}
35+
}
36+
37+
// in multiple components
38+
const person = getPerson(99)
39+
const info = getPersonInfo(person)
40+
// each time getPersonInfo() is called
41+
// it is re-run and creates redundant copies of its info object
42+
```
43+
44+
### Solution
45+
Reusable non-trivial computed/reactive objects in multiple components.
46+
47+
```ts
48+
// person-info.ts
49+
import { computed } from 'vue'
50+
import { watchRecordStore } from 'vue-cache-store'
51+
import { getPerson, getPersonInfo } from 'person-data.ts'
52+
53+
export const personInfo = watchRecordStore(
54+
// record watcher
55+
// auto clears cached object if returns falsy
56+
(id: number) => getPerson(id),
57+
// cached object creator
58+
(person: Person) => getPersonInfo(person),
59+
)
60+
```
61+
```ts
62+
// inside multiple components
63+
import { personInfo } from 'person-info.ts'
64+
65+
// returns reactive object
66+
const reactivePerson = personInfo.get(id)
67+
// ❌ dereferencing reactive objects breaks them
68+
const { lastName } = personInfo.get(id)
69+
// ✅ use the getRefs() instead
70+
const { firstName, fullName } = personInfo.getRefs(id)
71+
72+
const computedLastName = computed(() => personInfo.get(id).lastName)
73+
```
74+
1175
## Usage
1276

1377
### Define a Cache Store
1478
Cache stores are designed to behave similar to [Pinia](https://pinia.vuejs.org/) stores.
15-
The value returned by `usePersonCache()` can be used similar to Pinia.
79+
The value returned by `usePersonCache().get(id)` can be used similar to a Pinia store.
1680
```ts
1781
// person-cache.ts
1882
import { defineCacheStore } from 'vue-cache-store'
1983
import { computed } from 'vue'
20-
// example service
21-
import { getRecordInfo } from 'record-info-getter'
84+
85+
// simplified data source
86+
const people = ref([{
87+
id: 99,
88+
firstName: 'Jim',
89+
lastName: 'Kirk'
90+
}])
91+
92+
const getPerson = (id: number) => people.value.find(person => person.id === id)
2293

2394
export const usePersonCache = defineCacheStore((id) => {
24-
const info = getRecordInfo(id)
25-
const firstName = computed(() => info.firstName)
26-
const lastName = computed(() => info.lastName)
95+
const person = getPerson(id)
96+
const firstName = computed(() => person.firstName)
97+
const lastName = computed(() => person.lastName)
2798

2899
return {
29100
id: computed(() => id),
@@ -102,12 +173,14 @@ type CacheStore = {
102173
clear(): void,
103174
// increase use count by 1
104175
mount(): void,
105-
// decrease use count by 1
106-
// and clear if count is 0
107-
// and autoClearUnused option is true
176+
// decrease use count by 1
177+
// if autoClearUnused option is true,
178+
// calls clear(), clearing the whole store if count becomes 0
108179
unMount(): void,
109180
}
110181
const cache: CacheStore = usePersonCache()
182+
183+
const personInfo = cache.get(99)
111184
```
112185

113186
### Cache Store Context
@@ -141,39 +214,38 @@ Designed to cache an object store based on a record object.
141214

142215
#### `defineRecordStore()`
143216
Internally calls and returns `defineCacheStore()`
217+
144218
```ts
145219
// person-info.ts
146220
import { computed, ref } from 'vue'
147221
import { defineRecordStore } from 'vue-cache-store'
148222

149223
// minimal example
150-
type Person = {
151-
id: number,
152-
name: string,
153-
}
154-
155-
const people = ref<Person[]>([{
224+
const people = ref([{
156225
id: 99,
157-
name: 'Jim'
226+
name: 'Jim',
158227
}])
159228

160-
const getPerson = (id: number) => people.value.find(person => person.id === id)
161-
const removePerson = (id: number) => {
229+
const getPerson = (id) => people.value.find(person => person.id === id)
230+
const removePerson = (id) => {
162231
const index = people.value.findIndex(person => person.id === id)
163232
if (index > -1) {
164233
people.value.splice(index, 1)
165234
}
166235
}
167236
// defineRecordStore() internally calls and returns defineCacheStore()
168-
const usePersonInfo = defineRecordStore({
169-
getRecord(id: number) {
170-
// return value is watched
237+
export const usePersonInfo = defineRecordStore(
238+
// record watcher
239+
(id: number) => {
240+
// this function is watched
171241
// if the return value becomes falsy
172242
// the cached object is removed automatically
173243
return getPerson(id)
174244
},
175-
create(record: Person) {
176-
// return value of this function is cached
245+
246+
// cached object creator
247+
(record: Person) => {
248+
// return value of this function is cached.
177249
// even if used by multiple components
178250
// it will not be called repeatedly
179251
const { id: personId, name } = toRefs(record)
@@ -184,7 +256,7 @@ const usePersonInfo = defineRecordStore({
184256
nameLength: computed(() => record.name.length || 0),
185257
}
186258
},
187-
})
259+
)
188260

189261
const personInfo = usePersonInfo()
190262

@@ -214,37 +286,32 @@ personInfo.has(99) // false
214286
personInfo.ids() // []
215287
```
216288

217-
#### `makeRecordStore()`
289+
#### `watchRecordStore()`
218290
```ts
219-
import { makeRecordStore } from 'vue-cache-store'
291+
import { watchRecordStore } from 'vue-cache-store'
220292

221-
const personInfo = makeRecordStore({
222-
getRecord(id) {
223-
// ...
224-
},
225-
create(record) {
226-
// ...
227-
},
228-
})
229-
// proxy for
230-
const personInfoAlso = defineRecordStore(/* ... */)()
231-
// with more type clarity
293+
export const personInfo = watchRecordStore(/* ... */)
294+
295+
// watchRecordStore() internally does the following:
296+
const useInfo = defineRecordStore(/* ... */)()
297+
// with typing intact
298+
return useInfo()
232299
```
233300

234-
#### `makeRecordStore()` Usage with a [Pinia](https://pinia.vuejs.org/) store
301+
#### Usage within a [Pinia](https://pinia.vuejs.org/) store
235302

236303
```ts
237304
// person-store.ts
238305
import { defineStore } from 'pinia'
239-
import { makeRecordStore } from 'vue-cache-store'
306+
import { watchRecordStore } from 'vue-cache-store'
240307

241308
// minimal example
242309
type Person = {
243310
id: number,
244311
name: string,
245312
}
246313

247-
const usePersonStore = defineStore('people', () => {
314+
export const usePersonStore = defineStore('people', () => {
248315
const people = ref<Person[]>([{
249316
id: 99,
250317
name: 'Jim',
@@ -272,21 +339,19 @@ const usePersonStore = defineStore('people', () => {
272339
item.name = name
273340
}
274341

275-
const personInfo = makeRecordStore({
276-
getRecord(id: number) {
277-
return getPerson(id)
278-
},
279-
create(record: Person) {
342+
const personInfo = watchRecordStore(
343+
(id: number) => getPerson(id),
344+
(record: Person) => {
280345
const person = computed(() => record)
281-
const { name } = toRefs(record)
346+
const { id: personId, name } = toRefs(record)
282347

283348
return {
284-
id: computed(() => person.value.id),
349+
id: personId,
285350
name,
286351
nameLength: computed(() => person.value?.name.length || 0),
287352
}
288-
},
289-
})
353+
}
354+
)
290355

291356
return {
292357
people,
@@ -334,10 +399,10 @@ personStore.personInfo.ids() // []
334399

335400
When defining a cache store the second argument is a default options object.
336401

337-
| option | description |
338-
|:----------------------|:-------------------------------------------------------------------------------------------------|
339-
| `autoMountAndUnMount` | If true, automatically tracks the number of mounted components using the cache store |
340-
| `autoClearUnused` | If true, when there are no longer any mounted components using a cache store it will be cleared. |
402+
| option | description |
403+
|:----------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
404+
| `autoMountAndUnMount` | If true, automatically tracks the number of mounted components using the cache store. <br> Mounting is tracked when calling in the root of a component. Example: `const personInfo = usePersonInfo()` |
405+
| `autoClearUnused` | If true, when there are no longer any mounted components using a cache store it will be cleared. |
341406

342407
#### `defineCacheStore()` Options
343408
```ts
@@ -387,6 +452,7 @@ const options = {
387452
}
388453
```
389454
#### `defineRecordStore()` Options Usage
455+
390456
```ts
391457
// person-record.ts
392458
import { defineRecordStore } from 'vue-cache-store'
@@ -398,18 +464,18 @@ defineRecordStore.setGlobalDefaultOptions({
398464
})
399465

400466
// defining a record store with store default options overriding global defaults
401-
export const usePersonRecord = defineRecordStore({
402-
getRecord(id) {
403-
467+
export const usePersonRecord = defineRecordStore(
468+
(id) => {
469+
// ...
404470
},
405-
create(){
406-
471+
() => {
472+
// ...
407473
},
408-
defaultOptions: {
474+
{
409475
autoMountAndUnMount: false,
410476
autoClearUnused: false,
411-
}
412-
})
477+
},
478+
)
413479

414480
// inside a component
415481
// overrides usePersonRecord default options and defineRecordStore global defaults
@@ -418,6 +484,26 @@ const personCache = usePersonRecord({
418484
autoClearUnused: false,
419485
})
420486
```
487+
#### `watchRecordStore()` Options
488+
`watchRecordStore()` calls `defineRecordStore()` internally so it uses the global default options `defineRecordStore()`
489+
```ts
490+
import { watchRecordStore } from 'vue-cache-store'
491+
492+
// defining a record store with store default options overriding global defaults
493+
export const usePersonRecord = watchRecordStore(
494+
(id) => {
495+
// ...
496+
},
497+
() => {
498+
// ...
499+
},
500+
{
501+
autoMountAndUnMount: false,
502+
autoClearUnused: false,
503+
},
504+
)
505+
```
506+
421507

422508
### API
423509

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "vue-cache-store",
33
"type": "module",
4-
"version": "1.2.0",
4+
"version": "1.3.0",
55
"packageManager": "[email protected]",
66
"description": "Cache and re-use computed/reactive properties in vue",
77
"author": {

src/defineCacheStore.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ export interface CacheStore<T> {
2020
// increase use count by 1
2121
mount(): void,
2222
// decrease use count by 1
23-
// and clear if count is 0
24-
// and autoClearUnused option is true
23+
// if autoClearUnused option is true,
24+
// calls clear(), clearing the whole store if count becomes 0
2525
unMount(): void,
2626
options(): RequiredOptions,
2727
}

0 commit comments

Comments
 (0)