Skip to content

Commit 995601b

Browse files
Update no-active-pinia.md (#40)
1 parent e1d76cf commit 995601b

File tree

1 file changed

+128
-11
lines changed

1 file changed

+128
-11
lines changed

docs/faq/no-active-pinia.md

+128-11
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,71 @@ While this probably goes without saying, you should start by reading the page li
1515

1616
Before we do a deep dive into what might be going wrong here, let's briefly cover some common problems that have quick fixes.
1717

18-
### Quick fix 1 - missing `setup` attribute
18+
### Quick fix 1 - Call `app.use(pinia)` as early as possible
19+
20+
As implied by the error message, you might need to move the call to `app.use(pinia)` to an earlier point in your code.
21+
22+
In general, calls to `app.use(plugin)` need to happen before `app.mount(el)`, so the following example is unlikely to work for any plugin, not just Pinia:
23+
24+
```js
25+
const pinia = createPinia()
26+
const app = createApp(App)
27+
28+
// This is the wrong order, it won't work
29+
app.mount('#app') // [!code error]
30+
app.use(pinia) // [!code error]
31+
```
32+
33+
The order plugins are used also matters, depending on what each plugin does. This example is using Pinia and Vue Router:
34+
35+
```js
36+
const pinia = createPinia()
37+
const app = createApp(App)
38+
39+
// These are in the wrong order
40+
app.use(router) // [!code error]
41+
app.use(pinia) // [!code error]
42+
43+
app.mount('#app')
44+
```
45+
46+
That example is calling `app.use(router)` before `app.use(pinia)`. Most of the time that won't matter, but if something in the router tries to use the store it can fail.
47+
48+
In particular, the call to `app.use(router)` will immediately try to resolve any redirects. e.g.:
49+
50+
```js
51+
export default createRouter({
52+
// ...
53+
routes: [
54+
{
55+
path: '/',
56+
redirect() {
57+
const authStore = useAuthStore() // [!code highlight]
58+
return authStore.user ? '/home' : '/login'
59+
}
60+
},
61+
// ...
62+
]
63+
})
64+
```
65+
66+
The `redirect` function will be called as part of `app.use(router)`, before the call to `app.use(pinia)`. As a result, the call to `useAuthStore()` will fail.
67+
68+
Here's a Playground that shows this failing:
69+
70+
- [Broken Playground example](https://play.vuejs.org/#eNqNVE1P20AQ/Ssjt5IdKbGBphysQEMrpLZSWwSol7oHYw/xgr1r7a5JUJT/3tld23FCQD3kwzNvZt7Me8naq1LGwwflxR6raiE1rCGTmGq8qGvYwL0UFfhPDfoJ3wNcMc7SHlKbpy1IikajbHNh5B63adO8y6V1HboBCc8EVxpsLzgbzglGXZLgfYraBPSiXMKjCG4LVAipRGAcdIGwlIIvQMgcZcLNnEZh4LhQTRew42wPE6lEw3Xgv6Pv/sgbe2mji70D5XjPON5oQZP29084rizMsaX+F9TAYc+GlYFvOvtjWCccQGlaJ4ZgBGfnLgIgUTeS0zxqImPgTVnCxqTobWOpucsRtZnKJKs1KCqpzwdSXdtlfzNcDsWc9HrMIldJNfSgsapLIkJPALNBbUSRWdSnabbrcNA5P7AS8vkrU7Tm87gNumaHSfT1O9fqoOQQJ8EWeZkVwvLqEF2gN1IrAt07bUoSY8AhsOctHL34EOdgNDYQy0/F8MfJ0aoCUKe6iMGPfItySuVMYqZJvh4FrQMM+U7+4X7G0R2yVbqHGltK+EQzClGhD2ZaKRaM026uwvqAPlr7DGnZkp5aJuhkHLmO+6u9Xupm/Eet+fjbunB4e2PFHROt1/De3jE0M2BDlbs+copOqrQmhQWnBpZV0iZU4sUdz8Sb04goxyctRKkmac1MNvEKrWsVR1HD68dFSKyjF8D5aXgaTqOS3UWoqojxHFc0MPHaZRPP/oBf62eT85PwJPwY5WQSFwip1eROiiWptdvM2DvH6lV+XX5+FB5Pw+MjS8yRql40cr+Tt1o5xHwaTjt+2+gLkt0/CB1fK/LoPVvsnd7ozkqUv2rNyMM7EqRlKZbfbUzLBnuiWYHZ44H4g1o55leS/pzlEw6W06lcoHbpy5ufuKLvfbISeVMS+o3kNSpRNoajg31ueE60BzjL9pt1EuOLW3W50shVt5Qhaq9h8fbYX95YfUv3Qzjtr7j5B4vzcGA=)
71+
72+
The same applies if you're using other Vue plugins, not just Vue Router.
73+
74+
### Quick fix 2 - Missing `setup` attribute
1975

2076
If you're trying to use the store in a component with `<script setup>`, make sure haven't forgotten the `setup` attribute. If you just write `<script>` it'll run too soon.
2177

2278
For example:
2379

2480
```vue
25-
<script>
81+
<!-- This is wrong -->
82+
<script> // [!code error]
2683
import { ref } from 'vue'
2784
import { useProductsStore } from './products-store'
2885
@@ -45,7 +102,7 @@ Here it is in a Playground:
45102

46103
Change `<script>` to `<script setup>` and everything works fine.
47104

48-
### Quick fix 2 - Invoking the store by mistake
105+
### Quick fix 3 - Invoking the store by mistake
49106

50107
This example is using the Options API. The mistake is the line `...mapStores(useProductsStore())`. That should be `...mapStores(useProductsStore)` instead, without the parentheses. We need to pass in the function, not the store:
51108

@@ -62,7 +119,7 @@ export default {
62119
},
63120
computed: {
64121
// This is wrong...
65-
...mapStores(useProductsStore())
122+
...mapStores(useProductsStore()) // [!code error]
66123
}
67124
}
68125
</script>
@@ -78,15 +135,15 @@ export default {
78135

79136
- [Broken Playground example](https://play.vuejs.org/#eNp9VF1v2jAU/StWNokgFadf6gOiE9vUh03TVq17zEPd+AIujm3ZDkVC/Pdd20kIVPQBKb7n+vicY192Wc2Eoq8um2aiNtp6siOVBebhqzFkTxZW12S0aWBUqpOGR6EE61tMWB2awu4E0IIZQxNDqSqtnCdYIfeHc3L8jTssEvVoPCRHMJA0DvII4zpVat0on48+4fdonF20HiY1M+hJK3S1KxUhZQu4MpuSWAm1OYoqOGy81tJNmBEBLbOV98ZNi6JRZr2kla6Ld43zO3pHbwspXgpwdSEUhy0eWGYXHXeUeY4vgvNreo0cXDifChSpJi9Wvzmwx2R4/IRDfVZfh88v6dUtvbqMwpKoOhAFnn2p9hiQd5jyQixP4kEWIyTYP8YLvIWjmJiU+u1nrHnbQK+qWkG1TvUFk+4AvLptUvpoAb1sYGDFM7sEn+CHp9+wxe8erDVvJHZ/AP4Fp2UTRKa2b43iqHvQF+X+iNct1PKfe9h6UK5zFRzEOGJ/jPb7B94Pcm/o7SDG9kljgDNXWWH8l8F44Ot78hq9nxuOHcGX/GjRUOVdbO07aWHa+sQFIA4NbOM2DgvWSNwehHDmcS46pRZ8Y1W3IkTwKbm6vklL1NwZDvfceEC0baWU9nLzU1X5GActeS7VrOid4sJDbSTOJ64ImXGxiR/4+dJ4rxWZV1JU63schCEhXYCvVrng4zJrNxDySzPebi7S7kSKoxFYZ8XgLIz+OJ+Tvy6MSCg4jrQLv88x/c+8u4L74e581J0zukhZsSo+jz653soh9UCsJVCpl/lzsIUPkHzeCb5/jkF2V4EvS+3H2f4/xF7YQQ==)
80137

81-
### Quick fix 3 - Put it in a function
138+
### Quick fix 4 - Put it in a function
82139

83140
If the store call is outside a component, or in a component that isn't using `<script setup>`, make sure the call is inside a function. If it's in top-level code it will run too soon.
84141

85142
Here's an example using Vue Router:
86143

87144
```js
88145
// This won't work, because the call is at the top level
89-
const authStore = useAuthStore()
146+
const authStore = useAuthStore() // [!code error]
90147

91148
router.beforeEach((to) => {
92149
// Do something involving authStore
@@ -98,7 +155,7 @@ We can fix this by moving the store call inside the `beforeEach()` callback:
98155
```js
99156
router.beforeEach((to) => {
100157
// This should work
101-
const authStore = useAuthStore()
158+
const authStore = useAuthStore() // [!code highlight]
102159

103160
// Do something involving authStore
104161
})
@@ -130,7 +187,7 @@ The error is shown when step 3 occurs before step 2. The Pinia instance must be
130187

131188
- [Playground example](https://play.vuejs.org/#eNp9kUtvwjAQhP+KlQtpBXYfqAekSrRVD+2hRW2PuYRkCYb4IdsBJJT/3rVDwqOFW7Qz+2V2vI1EyiVd2GgUcaGVcWRLMgOpgyetSU1mRgnSW1XQS+SJYcIlT/skhxmX8O2Ugc6vvYQbicyUtI5UFiZG5VXmbGN8PFyLe3on9vpIZ9eEUkquGamv9ojWgquntPjAlmLqx/0F8dYzdgQfqhNDer+JGxSJcZA7kiqBlqqI29+iEPV3FQ1EqrEyJbG0bSIJSXaCTaIRCRM/G2NrLIeVU6q0g1RzrybR3DltR4xVUi8LminB/hjHD/SBDlnJpwysYFzmsMEfJlG/ZYew53hBHN/RO2Tk3LpmQBE1mBq1tmCOYfj7QQ7ibL5WH9/Q2yG9vQnBmlDCgzynTmSNBTmL5c14cVIPUjQvwXxqx7Hco5rSslTr9zBzpoIuVTaHbPnPfGE3TdCJATxlBQeXuNQU4Br59fsDNvjdiQJfskT3BfEL8OErn7GxPVcyx9gHvpD2Lbw2l8WPfd04kLY9ygcNbQR/aPblwun7uPd02LVY/wJpITTr)
132189

133-
But, in practice, it probably isn't that simple. In a real application, those 3 steps probably don't sit in the same file. More likely, they're in 3 separate files, something like this:
190+
But, in practice, it probably isn't that simple. In a real application, those 3 steps usually don't sit in the same file. More likely, they're in 3 separate files, something like this:
134191

135192
```js
136193
// useProductsStore.js
@@ -366,7 +423,7 @@ It's common to use a navigation guard to check whether a user is allowed to acce
366423

367424
```js
368425
// This won't work, because the call is at the top level
369-
const authStore = useAuthStore()
426+
const authStore = useAuthStore() // [!code error]
370427

371428
router.beforeEach((to) => {
372429
if (to.name !== 'login' && !authStore.isLoggedIn) {
@@ -380,7 +437,7 @@ Grabbing the store outside the callback seems like a good idea, as it avoids cal
380437
```js
381438
router.beforeEach((to) => {
382439
// This will work, as the call is now inside the callback
383-
const authStore = useAuthStore()
440+
const authStore = useAuthStore() // [!code highlight]
384441

385442
if (to.name !== 'login' && !authStore.isLoggedIn) {
386443
return { name: 'login' }
@@ -390,6 +447,66 @@ router.beforeEach((to) => {
390447

391448
Even if timing weren't an issue, there are other reasons why `useAuthStore()` should be called inside the navigation guard. Again, we'll discuss those [later](#why-does-pinia-work-this-way).
392449

450+
### Using a property getter
451+
452+
[Property getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get) are declared using functions, so in some cases they can be used to defer the store call.
453+
454+
Consider this example:
455+
456+
```js
457+
import { useHistoryStore } from './history-store'
458+
459+
export default {
460+
menuItems: [
461+
{
462+
text: 'Save',
463+
data: useHistoryStore().history // This won't work // [!code error]
464+
},
465+
// ...
466+
]
467+
}
468+
```
469+
470+
The call to `useHistoryStore()` is in top-level code, so we need to move it to a function. One way to achieve that would be to use a property getter:
471+
472+
```js
473+
export default {
474+
menuItems: [
475+
{
476+
text: 'Save',
477+
get data() { // [!code highlight]
478+
return useHistoryStore().history // [!code highlight]
479+
} // [!code highlight]
480+
},
481+
// ...
482+
]
483+
}
484+
```
485+
486+
Note the use of the `get` keyword to declare the property. The function will be called automatically when someone tries to access the `data` property, so the consuming code won't need to worry about the function call.
487+
488+
### Using `computed()` or `toRef()`
489+
490+
In the previous example we were working with non-reactive data, so we used a property getter.
491+
492+
For properties inside reactive objects we can use `computed()` or `toRef()` to achieve a similar result. For example:
493+
494+
```js
495+
export default reactive({
496+
menuItems: [
497+
{
498+
text: 'Save',
499+
data: computed(() => useHistoryStore().history) // [!code highlight]
500+
// Alternatively:
501+
// data: toRef(() => useHistoryStore().history)
502+
},
503+
// ...
504+
]
505+
})
506+
```
507+
508+
The `computed` ref will be automatically unwrapped by the proxy created by `reactive()`, so the consuming code doesn't need to use `.value` to access the value. We could also have used `ref()` instead of `reactive()`, the unwrapping of the nested ref would work the same way.
509+
393510
### An example with a component
394511

395512
This component is using an explicit `setup` function. Again, it might seem reasonable to put the call to `useProductsStore()` outside `setup`. But that makes it top-level code, so it'll run too soon:
@@ -399,7 +516,7 @@ This component is using an explicit `setup` function. Again, it might seem reaso
399516
import { ref } from 'vue'
400517
import { useProductsStore } from './products-store'
401518
402-
const products = useProductsStore()
519+
const products = useProductsStore() // [!code error]
403520
404521
export default {
405522
setup() {

0 commit comments

Comments
 (0)