Determine whether or not a pinia store is being actively used by a component. #669
Replies: 3 comments 15 replies
-
I'm glad you are enjoying pinia 😄 |
Beta Was this translation helpful? Give feedback.
-
Hey, unfortunately no, I was never able to figure it out.
…On Tue, Sep 6, 2022 at 1:06 PM Theo Ephraim ***@***.***> wrote:
@tochoromero <https://github.com/tochoromero> - did you ever get this
figured out? Looking to do something similar with opening/closing
websockets while the store is being used. Thanks!
—
Reply to this email directly, view it on GitHub
<#669 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABAEDP2IN3MK3PNTYJ52TQTV46P4PANCNFSM5D23ZRYQ>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
@posva @tochoromero - I took a stab at this and got something sort of working. It works well to track components that use a store directly - meaning they call The problem is that the composition model of pinia is that stores can use each other directly within getters. So while this is fine (a store being used directly in a setup function) const store = useCounterStore();
const count = computed(() => { return store.counter; }); This is not - and is equivalent to what we are doing when we use a store getter that uses another internally const count = computed(() => {
// no longer considered within the setup function, cannot call lifecycle hooks here
const store = useCounterStore();
return store.counter;
}); This unfortunately triggers a warning and does not work properly. The warning: Maybe there is a workaround is to require these stores to declare how they use each other rather than relying on the useStore calls within getters. However I'd still need to detect if the useStore call is coming within a setup function or not, and I don't know if that is possible, as it seems Or we would need a way of dynamically injecting more mount/unmount behaviour into the component outside of the setup function, which I'm not sure is possible? I do think this kind of functionality could be very useful - and likely should be baked into pinia itself. Would also be useful for initializing watchers and other kinds of behaviour - see this discussion. @posva - any ideas of workarounds or if it's even possible? Thanks!!! The code (full working example up here: https://stackblitz.com/edit/vitejs-vite-1czpyx?file=src/CounterWidget.vue) Plugin: import { defineStore, PiniaPlugin, PiniaPluginContext } from 'pinia';
import { getCurrentInstance, onBeforeMount, onBeforeUnmount, ref } from 'vue';
declare module 'pinia' {
export interface DefineStoreOptionsBase<S, Store> {
// adds our new custom options for activation / deactivation hooks
onActivated?: (this: Store) => void | Promise<void>;
onDeactivated?: (this: Store) => void | Promise<void>;
}
}
export function addStoreHooks<T extends ReturnType<typeof defineStore>>(
useStoreFn: T
) {
return (...args: Parameters<T>): ReturnType<T> => {
// NOTE - was fighting against the TS types in here, but couldn't quite get it right
// however it's not important to have it working here versus in _consumers_ of the store
// and it does all work correctly there
const store = useStoreFn(...(args as any[])) as any;
const component = getCurrentInstance();
if (component) {
onBeforeMount(() => { store.incrementUseCounter(); });
onBeforeUnmount(() => { store.decrementUseCounter(); });
}
return store;
};
}
export const piniaHooksPlugin: PiniaPlugin = ({
pinia,
app,
store,
options: storeOptions,
}: PiniaPluginContext) => {
// might not need this check, but not sure this plugin code is guaranteed to only be called once
if (store._numStoreUsers) return;
// attach new counter to track number of components using this store
store._numStoreUsers = ref(0);
// expose our new store property in devtools
if (import.meta.env.DEV) {
store._customProperties.add('_numStoreUsers');
}
store.incrementUseCounter = () => {
store._numStoreUsers++;
if (store._numStoreUsers === 1 && storeOptions.onActivated) {
storeOptions.onActivated.call(store);
}
};
store.decrementUseCounter = () => {
store._numStoreUsers--;
if (store._numStoreUsers === 0 && storeOptions.onDeactivated) {
storeOptions.onDeactivated.call(store);
}
};
}; The plugin adds store options Note that this requires defining the store now looks like: export const useCounterStore = addStoreHooks(defineStore("counter", {... This extra requirement is not ideal but is totally reasonable. I had tried to augment defineStore directly but it does not seem possible... |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Here is my use case.
We use composition functions to define our stores and we use SWRV heavily, interestingly enough @posva also used it on his composition store example.
Now lets say I have a composition store that uses SWRV to fetch expensive data from my server and it refreshes the data every minute.
The data will not be initially fetched until a component uses the store for the first time, this is good, but because the stores are long lived singletons, the SWRV instance will continue to fetch the data every minute even after the component that originally called the store has been unmounted.
SWRV allows you to pass a reactive function that either returns the key for the SWRV call or
null
if it should not fetch data. We can leverage this in userland in a couple of ways:But this adds a burden on the developers and if you forget to register or unregister you will not get the expected behavior.
I had this working but I couldn't make the typing work, the returned object from the store completely lost its typing and I couldn't figure out how to maintain it.
Now, I believe there is clean solution that can be baked into Pinia itself.
What if Pinia internals maintained a list of callers for the store, this should be possible using the
getCurrentInstance
function andonBeforeUnmounted
hooks. Then somehow expose ahasDepandants
(or something similar) to the store so we can know whether or not anybody is using the store.I believe this will work even for stores calling other stores because nested stores will use the same mechanism to register/unregister the dependant.
So yeah, this are my thoughts. Or maybe there is a cleaner way to achieve the same way in user land that I haven't thought about, I just want to make it as transparent as possible for my developers.
Thank you for your awesome work, you are really pushing the ecosystem with your state management implementation.
Beta Was this translation helpful? Give feedback.
All reactions