pinia
pinia copied to clipboard
Allow changing pinia root store for component scope
What problem is this solving
When documenting a component with storybook and its docs addon, multiple stories are rendered in one MDX document. That means they are technically part of the same Vue application. Semantically however, each story represents a very specific state of the component. Usually these components would not access any global state (and thus pinia) at all, but sometimes it is necessary. That means each of the story components needs its own, independant βglobalβ state.
Proposed solution
Pinia should allow to change the root store Pinia instance which is injected into a component and its descendents.
Technically this could be done simply by exposing the piniaSymbol
and than I could use provide(piniaSymbol, createPinia())
.
On the other hand it might be more prudent to keep the symbol private and expose a convenience function providePinia(pinia) { provide(piniaSymbol, pinia ?? createPinia()) }
.
Describe alternatives you've considered
It is also possible to patch alias fields into the storybook webpack config and mock the store definition module which provides the useMyStore
function for each store individually. Then each story could set a currentMock property in those modules.
This would be much more complex however, harder to understand and possibly fragile.
Probably a providePinia(pinia)
(the argument should be mandatory) is the best way so the current app and other properties can be set (like in pinia.install). ~~This should be an easy contribution!~~
This is like nested pinias
I notice the code below
const piniaSymbol = ((process.env.NODE_ENV !== 'production') ? Symbol('pinia') : /* istanbul ignore next */ Symbol());
you can just change Symbol('pinia')
to Symbol.for('pinia')
.
Symbol.for('pinia')
allow developer overwrite the Provide by using Vue's provider with the Symbol.for('pinia')
(because Symbol.for('pinia') equal Symbol.for('pinia')
)
or export const piniaSymbol
I'm also looking for something like this, but for a different use-case. I want to explain here, and maybe get some opinions about it.
TL;DR I don't want global state across all pages in the application, but "global state" on each page.
We have a quite large application, with many pages (around 200), all of which are kind of "single page applications" themselves, with data, filters, side bars, popups etc. There is a big navigation menu, and when the user clicks on a different page in the menu, the expected behavior is that the destination page opens with a clean state. If they navigate back to the previous page, it's not expected to maintain the state that it had before (unless it's explicitly set up to do that via some persistence mechanism).
Basically, it's supposed to work as if each menu click re-loads the page. However, we're using vue-router to navigate between the pages without re-loading, because it gives a much more responsive experience.
We're not currently using any state management library, but just a combination of component state, and ad-hoc use of provide/inject on the different pages. I think it would be beneficial to have something like pinia, if we could make each page create a completely independent store, which is disposed when navigating away from the page.
We could perhaps achieve this by calling $reset
when opening a page, but it doesn't feel natural. We would easily forget to do it, and create unwanted behavior that would be missed by QA. Also, it seems wasteful to have a big store holding state from all previously visited pages, that we don't need.
I think something like providePinia
, would be perfect for our use-case. Alternatively, if the Pinia
instance provided a global reset function, that basically destroyed all registered stores, and we could hook that into the router, perhaps that would work.
A similar use case is having multiple, complex components in a single page (or nested components -> same app), whereas each component should handle its own pinia instance/store.
+1 on this! I think this is a great feature. Simplifying complex nested components with a store is great for keeping things clean. But having the store be a global/singleton means that component is then a bit less reusable/isolated.
Being able to 'scope' your stores to components means the components then are completely self contained/reusable and benefit from the cleaner architecture that using something like pinia provides π
Then you could have something neat like (complete pseudocode & probably not the best way to achieve).
<script setup>
...
const props = {
myId: number
}
const piniaInstance = createPinia()
const componentStore = useComponentStore(piniaInstance)
componentStore.load(myId)
</script>
<template>
<sub-one :store="componentStore" />
<sub-two :store="componentStore" />
</template>
Because currently you would have something like this in a sub component:
<script setup>
...
const componentStore = useComponentStore()
</script>
<template>
... use the componentStore
</template>
But now the sub component isn't self-documenting or that reusable... it assumes that the store has been loaded with the right data. How do we know what component is in charge of loading the store etc? You can't show two of the component with different data etc.
When multiple components
want to use same store
, or a component that with a store will render multiple times
, the reusable
of store
is important.
Now, i can't find a solution in pinia, so i write a tool to reuse store
, the repo is here: pinia-di.
I'm also looking for something like this, but for a different use-case. I want to explain here, and maybe get some opinions about it.
TL;DR I don't want global state across all pages in the application, but "global state" on each page.
We have a quite large application, with many pages (around 200), all of which are kind of "single page applications" themselves, with data, filters, side bars, popups etc. There is a big navigation menu, and when the user clicks on a different page in the menu, the expected behavior is that the destination page opens with a clean state. If they navigate back to the previous page, it's not expected to maintain the state that it had before (unless it's explicitly set up to do that via some persistence mechanism).
Basically, it's supposed to work as if each menu click re-loads the page. However, we're using vue-router to navigate between the pages without re-loading, because it gives a much more responsive experience.
We're not currently using any state management library, but just a combination of component state, and ad-hoc use of provide/inject on the different pages. I think it would be beneficial to have something like pinia, if we could make each page create a completely independent store, which is disposed when navigating away from the page.
We could perhaps achieve this by calling
$reset
when opening a page, but it doesn't feel natural. We would easily forget to do it, and create unwanted behavior that would be missed by QA. Also, it seems wasteful to have a big store holding state from all previously visited pages, that we don't need.I think something like
providePinia
, would be perfect for our use-case. Alternatively, if thePinia
instance provided a global reset function, that basically destroyed all registered stores, and we could hook that into the router, perhaps that would work.
On this, rather than a pinia-side change; you could have an enforced version of the $reset workflow you're suggesting so that it couldn't be forgotten (I'm aware you're saying this feels unnatural, which is fair):
- Add a RouteMeta interface in vue-router with a required property called 'store'
- Update all routes to provide that store's useXXXStore function
- In a vue-router's before guard, call the route's meta.store property to get the page's store and then call $reset on it.
This automates the process you've described - although relies on you being aware that if you (for some reason) access a different page's store - that any changes made to that store won't be $reset but will be reset by accessing that different page.