vuejs / pinia Goto Github PK
View Code? Open in Web Editor NEW๐ Intuitive, type safe, light and flexible Store for Vue using the composition api with DevTools support
Home Page: https://pinia.vuejs.org
License: MIT License
๐ Intuitive, type safe, light and flexible Store for Vue using the composition api with DevTools support
Home Page: https://pinia.vuejs.org
License: MIT License
There is currently no documented way to access Vue root plugins, such as this.$axios
or this.$router
like I might do in Nuxt. I feel that this is a very common use case since the Vue plugin ecosystem is large.
Since binding this
is not the way forward, can we have a way to get at the root? I could see it exposed similar to how context is in setup()
. I can't think of a way to achieve backwards compatibility with the current API. I also don't see how to access it since the function is used directly (imported and called) and there's no API on the outside. This makes me think that the API would have to exist on the inside of the function much like accessing a store is done:
export async function appendFile(file: File): Promise<void> {
const store = useFilesStore();
store.state.all.push(file);
}
Perhaps plugins have to be defined differently in order to be accessed here. All existing plugins would either define a new composition API or a dev could define their own composition wrapper on top of any that haven't converted.
Project example for typescript implementation
In my application, I return a user object when authenticated:
{
"id": 1,
"username": "googlemac",
"hasAccess": true,
"roles": {
"isAdmin": true,
},
}
My issue is that updating the user state with store.patch()
does not have the desired effect for "deep" data. All of the data is correctly updated (shown in devtools) and the username (regular state) in the UI is updated, but trying to reference state.roles.isAdmin
is not reactive. The getter is also not reactive. If I instead update the state directly, accessing the state directly or through the getter works properly.
I have defined my store:
function state() {
return {
employeeId: 0,
username: '',
hasAccess: false,
roles: {},
} as User;
}
export type RootState = ReturnType<typeof state>;
const getters = {
isRestricted: (state: RootState) => !state.hasAccess,
isAdmin: (state: RootState) => state.roles?.isAdmin ?? false,
};
export const fetchUser = async () => {
const store = useUserStore();
const { data } = await axios.get('/api/user');
const { user } = data;
// NOTE: this does not work!
// store.patch(user);
// NOTE: this works as desired
store.state.employeeId = user.employeeId;
store.state.username = user.username;
store.state.hasAccess = user.hasAccess;
store.state.roles = user.roles;
};
Also, is there a way to make the getter state
already typed with the root state? It's only one line and not that big of a deal, but it may help. If I don't put this, I get a TS error that it is any
type.
We have a package A with pinia dependency and we have a package B with a package A dependency.
If you install normally via npm it works. But if you link package A with npm link only the stores in package A still work. Packages in B are missing the "register module โ ModuleName" console logs and do not show up in devtools. The mutations show up in devtools but no actual data is found as the store are not found. No error message is displayed.
webpack-internal:///./.nuxt/client.js:136 TypeError: Cannot read property 'pinia' of undefined
at eval (webpack-internal:///./.nuxt/pinia.js:35)
at getInitialState (webpack-internal:///./node_modules/pinia/dist/pinia.esm.js:125)
at useStore (webpack-internal:///./node_modules/pinia/dist/pinia.esm.js:263)
at useApi (webpack-internal:///./client/utils/api.ts:241)
at setup (webpack-internal:///./node_modules/babel-loader/lib/index.js?!./node_modules/ts-loader/index.js?!./node_modules/vue-loader/lib/index.js?!./client/components/layout/topbar/Topbar.vue?vue&type=script&lang=ts&:176)
at mergedSetupFn (webpack-internal:///./node_modules/@vue/composition-api/dist/vue-composition-api.module.js:443)
at $options.setup (webpack-internal:///./.nuxt/pinia.js:22)
at eval (webpack-internal:///./node_modules/@vue/composition-api/dist/vue-composition-api.module.js:662)
at activateCurrentInstance (webpack-internal:///./node_modules/@vue/composition-api/dist/vue-composition-api.module.js:594)
at initSetup
It crashes on this line because nuxtState
seems to be undefined
in SPA mode.
https://github.com/posva/pinia/blob/master/nuxt/plugin.js#L20
In universal
mode it's all good:)
Hi there! I had the need to mock an imported useStore
function in my unit tests, and was having some trouble with setting up the mocks in the test properly. I came up with the following solution:
component.vue
<template>
<button @click="addOne">Add 1</button>
</template>
<script lang="ts">
import { defineComponent } from '@vue/composition-api';
import { useCounterStore } from '../stores/counter';
export default defineComponent({
setup() {
const counterStore = useCounterStore();
return { addOne };
function addOne() {
counterStore.add(1);
}
}
});
</script>
component.test.js
import { shallowMount } from '@vue/test-utils';
import CounterComponent from './component.vue';
import { useCounterStore } from '../stores/counter';
describe('Component', () => {
it('Calls the add method on the counter store', () => {
const counterStore = useCounterStore({});
jest.spyOn(counterStore, 'add');
const component = shallowMount(CounterComponent);
component.find('button').trigger('click');
expect(counterStore.add).toHaveBeenCalledWith(1);
});
});
The object in the first param in a getter used to be a state. Now (in 0.1.0) it seems to be the whole store, yet the TS type for it still is DataState
. Readme suggests using this
in getters but I still like the shorthand arrow functions:
getters: {
admins: state => state.users.filter(user => user.role === 'admin'),
}
Noticed that Pinia's next branch has not been updated in a while and is a few commits behind the master branch (for Vue 2). As Vue 3 should be released soon-ish, will Pinia's next branch be updated in particular to support the new Vue-dev-tool-next which has timeline and plugin support?
pinia: 0.0.2
Vue DevTools : 5.3.3
In Vue DevTools there is an option named "New Vuex Backend" that is bringing performance boost.
But it seems there's an issue when using pinia
and that it's enabled.
It seems there's an issue about keeping the old state, which make "Commit", "Revert" & "Travel Time" not working.
Toggling off the option fix the issues.
I've inspected Vue DevTools code and it seems that the "old" one turned legacy, at least around variable names in else
statements here :
https://github.com/vuejs/vue-devtools/blob/dev/packages/app-backend/src/vuex.js#L128
https://github.com/vuejs/vue-devtools/blob/dev/packages/app-backend/src/vuex.js#L207
So I don't know if it's planned to be removed, but I guess that for now :
pinia
isn't fully compatible when the the option is enabledvue-devtools
may have an issue around states when the option is enabledpinia
(but dunno, as things turn to become legacy around vue-devtools
)Still, awesome work @posva !
https://github.com/posva/pinia/blob/master/nuxt/index.js#L8
Is Pinia conflicting with VueX and it has to be turned off or is this just for the convienience?
This makes gradual migration from VueX to Pinia a bit difficult.
My guess is that Pinia is quite well encapsulated so it can totally live side by side to Vuex, there shouldn't be any conflicts on the context.
So we have a very big and complicated order management app with 7 different stores and price recalculations on IE11 take up to 30s compared 200ms on iOS. When we removed pinia as a store and used Vue.observable() instead it went down to 30ms on IE11 and very fast on iOS
Hello, first of all I would like to say that I love pinia and this direction and I'm actually trying to use it in production with Vue2 and composition-api library.
I have an issue which I would like to help with. Issue can be easily reproduced with new vue-cli project, router with history mode, Typescript and Babel without class components.
Install composition-api and pinia for Vue2 and register composition-api in main.ts by Vue.use()
.
Next just need two component, store from documentation here on github and router.
stores/test-store.ts
import { createStore } from "pinia"; // btw can't find defineComponent method
export const useMainStore = createStore({
// name of the store
// it is used in devtools and allows restoring state
id: "main",
// a function that returns a fresh state
state: () => ({
counter: 0,
name: "Eduardo",
}),
// optional getters
getters: {
doubleCount() {
return this.counter * 2;
},
doubleCountPlusOne() {
return this.doubleCount * 2;
},
},
// optional actions
actions: {
reset() {
// `this` is the store instance
this.counter = 0;
},
},
});
views/MainComponent.vue
<template>
<div>
<h1>{{ main.counter }}</h1>
<router-link to="/second" tag="button">go</router-link>
<button @click="main.counter++">click</button>
</div>
</template>
<script>
import { useMainStore } from "@/stores/test-store";
import { computed, defineComponent } from "@vue/composition-api";
export default defineComponent({
setup() {
const main = useMainStore();
console.log(main.counter);
main.counter = main.counter + 1;
console.log("count");
console.log(main.counter);
return {
// gives access to the whole store
main,
// gives access only to specific state
state: computed(() => main.counter),
// gives access to specific getter; like `computed` properties
doubleCount: computed(() => main.doubleCount),
};
},
});
</script>
views/SecondComponent.vue
<template>
<div>
<h1>Test</h1>
</div>
</template>
<script>
import { computed, defineComponent } from "@vue/composition-api";
export default defineComponent({
setup() {
return {};
},
});
</script>
router/index.ts
import Vue from "vue";
import VueRouter, { RouteConfig } from "vue-router";
import MainComponent from "../views/MainComponent.vue";
import SecondComponent from "../views/SecondComponent.vue";
Vue.use(VueRouter);
const routes: Array<RouteConfig> = [
{
path: "/",
name: "Main",
component: MainComponent,
},
{
path: "/second",
name: "Second",
component: SecondComponent,
},
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes,
});
export default router;
After setting up the app there are to buttons, go to next route button and click button, there is also logic in MainComponent.vue setup() function to increment counter every time and to console.log() to make sure setup function is being called.
When you serve the app everything works fine, counter gets automatically incremented to 1 and when you click on button it keeps incrementing. However when you get to the second route using go button and back using history back button, setup function is triggered but counter is not incremented and click button also not incrementing anymore. Event of the click button is being fired just reactivity of store state seems broken. (even if you not use browser back history btn but setup router-link in the next component and redirect back it's gonna be the same).
Any ideas what could be the issue and potential workaround?
Thank you very much for help!
I am trying to create generic function for creating base stores. Something like this
interface BaseState {
baseData: ""
};
function defineEnumerationStore<TEnumeration, TAdditionalState, TAdditionalActions>(id: string, additionalState?: TAdditionalState, additionalActions?: TAdditionalActions) {
return defineStore({
id: "...",
state: () => ({
baseData: "",
...additionalState
} as BaseState & TAdditionalState),
actions: {
baseMethod() {
return this.baseData;
},
...additionalActions
} //here I can't type actions object like StoreWithState<> & TAdditionalActions
})
}
I want to be able to provide additional actions and state data to this generic function. My problem is that I can't type my extended actions object like shown in the code above. Maybe I am doing it wrong way, but I think it would help if StoreWithState interface was exported so I can use it to type my actions object.
Export type:
interface StoreWithState<Id extends string, S extends StateTree>
Not trying to offend, trying to understand do we really need a state management library for Vue 3?
I mean, the example code in the first page:
import { createStore } from 'pinia'
export const useMainStore = createStore({
// name of the store
// it is used in devtools and allows restoring state
id: 'main',
// a function that returns a fresh state
state: () => ({
counter: 0,
name: 'Eduardo',
}),
// optional getters
getters: {
doubleCount: (state, getters) => state.counter * 2,
// use getters in other getters
doubleCountPlusOne: (state, { doubleCount }) => doubleCount.value * 2,
},
// optional actions
actions: {
reset() {
// `this` is the store instance
this.state.counter = 0
},
},
})
Can be replaced with:
import { computed, reactive } from '@vue/composition-api';
const state = reactive({
counter: 0,
name: 'Eduardo',
});
// Getters / Computed
const doubleCount = computed(() => state.counter * 2);
const doubleCountPlusOne = computed(() => doubleCount * 2 + 1);
// Actions
const reset = () => state.counter = 0;
export { state, doubleCount, doubleCountPlusOne, reset };
and it will work mostly the same.
Are there any advantages for pinia over the example I attached ?
Micro issue, you say:
"Pinia is is the most similar English pronunciation of the word pineapple in Spanish: piรฑa."
"Pinia" would sound phonetically like 'peen-ee-aah', or Pin-ee-ya'.
May I suggest 'peenya' as a replacement.
It might read like this:
"Piรฑa means pineapple in Spanish, in English it would sound like 'peenya'.
Thank you for your work.
Currently the latest NPM version is 0.1.0, which still uses the old API for creating and registering stores. Following the README tutorial gives errors since these changes have not yet been published to NPM.
If these changes are something you are still working on, sorry for this issue, but I wanted to create this in case it was missed.
https://github.com/posva/pinia/blob/765e487a19a8563f5607d0fadd0ea4efce3e1f59/src/store.ts#L71
According to the the docs, watch
should provide a previous value so it could be passed in subscriptions. It could then be used to create a diff in DevTools or logging utility.
Hi, thanks for the great work!!
I have a question for the getters in pinia.
Can I pass arguments to getters just like the vuex's getter? https://vuex.vuejs.org/guide/getters.html#method-style-access
Thanks.
Error: Exported variable 'useMainStore' has or is using name 'StoreWithState' from external module "/Users/steven/Documents/New Documents Dec 2020/boilerplate/versiontwo/node_modules/pinia/dist/pinia" but cannot be named.ts(4023)
Installed pinia@next and added it to main.ts and created a new file with the example code.
main.ts
import { createApp } from "vue";
import "./assets/main.css";
import App from "./App.vue";
import { routes } from "./routes";
import { createRouter, createWebHistory } from "vue-router";
import { store } from "./store";
import { createPinia } from "pinia";
const app = createApp(App);
app.use(createPinia());
app.use(store);
export const router = createRouter({
history: createWebHistory(),
routes,
});
app.use(router);
app.mount("#app");
piniaStore.ts
import { defineStore } from "pinia";
export const useMainStore = defineStore({
// name of the store
// it is used in devtools and allows restoring state
id: "main",
// a function that returns a fresh state
state: () => ({
counter: 0,
name: "Eduardo",
}),
// optional getters
getters: {
doubleCount() {
return this.counter * 2;
},
// use getters in other getters
doubleCountPlusOne() {
return this.doubleCount * 2;
},
},
// optional actions
actions: {
reset() {
// `this` is the store instance
this.counter = 0;
},
},
});
Hello,
First: thanks for the work put in this library, it really is a fresh breeze after using vuex for some time.
To the point:
I have just recently updated our project to new version of nuxt (v.2.13) and I have encountered a problem with pinia.
Here is the transcript of the error:
TypeError: Cannot read property 'control' of undefined
at getInitialState (pinia.esm.js?d071:119)
at useStore (pinia.esm.js?d071:257)
at setup (default.vue?2e42:40)
at mergedSetupFn (vue-composition-api.module.js?750b:418)
at eval (vue-composition-api.module.js?750b:637)
at activateCurrentInstance (vue-composition-api.module.js?750b:569)
at initSetup (vue-composition-api.module.js?750b:636)
at VueComponent.wrappedData (vue-composition-api.module.js?750b:623)
at getData (vue.runtime.esm.js?2b0e:4748)
at initData (vue.runtime.esm.js?2b0e:4705)
It seems like an issue in this function:
function getInitialState(id) {
var provider = stateProviders.get(getActiveReq());
return provider && provider()[id];
}
I am not sure, but it seems to me it could be somehow related to work done on integrating the composition-api guys just recently did in the nuxt project.
Any insights on how to make this work again?
Thanks!
Hi,
I'm struggling to make the state type-aware of my TS interfaces
types.d.ts
export interface Rule {
name: string;
category: string;
}
rule-store.ts
export const useRuleStore = createStore(
'rules',
() => ({
rules: [] as Rule[],
}),
{},
);
I get a TS error :
Type 'Rule[]' is not assignable to type 'StateTreeValue'.
Type 'Rule[]' is not assignable to type 'StateTreeArray'.
The types returned by 'pop()' are incompatible between these types.
Type 'Rule | undefined' is not assignable to type 'string | number | boolean | symbol | void | Function | StateTree | StateTreeArray | JSONSerializable | null | undefined'.
Type 'Rule' is not assignable to type 'string | number | boolean | symbol | void | Function | StateTree | StateTreeArray | JSONSerializable | null | undefined'.
Type 'Rule' is not assignable to type 'StateTree'.
Index signature is missing in type 'Rule'.ts(2322)
types.d.ts(6, 18): The expected type comes from property 'rules' which is declared here on type 'StateTree'
If i replace rule-store.ts by the following:
export const useRuleStore = createStore(
'rules',
() => ({
rules: [] as any[],
}),
{},
);
It works, but then i lose the main purpose of using TS as state.rules will not be typed ...
What am I doing wrong ?
Thanks for the help !
Best regards
I want to separate my getters and actions to dedicated files instead of just put them all together in one store file, like this:
import { defineStore } from 'pinia'
import { state } from './state'
import { getters } from './getters'
import { actions } from './actions'
export const useMainStore = defineStore({
id: 'main',
state: () => state,
getters,
actions,
})
But in the getters and actions file, I can not use this
to reference the store object now.
What should I do for such a situation?
Thanks!
I'm trying to use ESM file directly from unpkg.com
. The problem is that pinia.esm.js
and pinia.esm.min.js
both include import { ref, watch, computed } from '@vue/composition-api';
.
Is this intentional? I'm actually trying it with freshly published vue 3 build.
I see that @vue/composition-api
is defined as global in rollup config. Maybe for vue 3 we'd need another build that doesn't rely on @vue/composition-api
.
Well i have 2 pages that use useLoginStore
//layout.vue
useLoginStore({ onSuccessLogin: () => console.log('test layout.vue') });
//login.vue
const loginStore = useLoginStore({
onSuccessLogin: () => {
console.log("teste login.vue");
vm.root.$router.push({ name: "home" });
},
});
In the first time everything is ok, but if i reload when i go to login.vue he executes the onSucessLogin
from the layout.vue
.
My useLoginStore
is:
export const useLoginStore = (params: { onSuccessLogin?: Function }) =>
createStore({
id: "login",
state: () => ({
loading: false,
loadingUser: false,
user: {
id: null as number | null,
email: "",
},
}),
getters: {
userEmail: (state) => state.user.email,
userFirstLetter: (state) => state.user.email.slice(0, 1),
},
actions: {
async submit(form: Login) {
if (params.onSuccessLogin) params.onSuccessLogin();",
},
},
})();
A way to create plugin to pinia. I used in my last project the vuex cache plugin and i would want for a way to create a plugin. Maybe something like:
// random-project.js
import { usePlugin } from 'pinia'
import piniaCache from 'pinia-cache'
usePlugin(piniaCache)
// will track both stores
const a = createStore(...)
const b = createStore(...)
// pinia-cache-example.js
export function piniaCache(pinia){
pinia.subscribe((mutation, state)=> ...)
}
This is like a listen store but global to track all state. I dont know if this is the best way but is important have ways to extend the main behavior ๐
i trying to examine pinia in existing project with vue 2.6.12, vuex 3.6.0 , @vue/composition-api: 1.0.0-beta.25 . i can see state in console, but not see in dev tools. Should i some register in main.ts (App.use) or dev tools support not work with existing vuex?
you have shared this URL in Github releases for changelong but it's not working.
please check and fix.
Hi,
i'm newest on Vue and Pinia bit this Code is great.
My question is, Exists support for permanent storage or and example how implement with pinia and vue3?
In the readme, you mention
Should the state be merged at the same level as actions and getters?
I figured we should open a discussion around this ๐
My 2 cents:
I think we should either scope all things into relevant properties, eg:
store.state.counter
store.getters.doubleCount
store.actions.reset()
or scope none
store.counter // state
store.doubleCount // getter
store.reset // action
Having them all merged in the "root" store might cause naming conflicts between getters and state. However, you could make the argument that having a getter with the exact same name as a property in the state is a fairly bad design decision to begin with. In the case of a naming conflict, the state one should probably overwrite the getter, as getters will often rely on the state value to produce their own value.
A big pro for having them all in the root is that you would be able to do things like
import { useStore } from '@/stores/my-store';
export default createComponent({
setup() {
const { counter, doubleCount, reset } = useStore();
}
});
where you can destructure any of the 'items' in the store.
Is there any specific reason why you decided on putting actions / getters in the root currently @posva?
Hi there!
What would need to be done in order to support getters with parameters, and how should they work ideally?
How do they differ from actions? Is there a way where you could return a computed
from an action in order to mimic the same functionality?
I just gave Pinia a quick try in a Vue 3 app and noticed I couldn't use actions:
import { defineStore } from "pinia";
export const useSessionStore = defineStore({
id: 'session',
state: () => ({
isLoggedIn: false,
}),
actions: {
setLoggedIn() {
this.isLoggedIn = true;
}
}
});
console.log(Object.keys(sessionStore));
//ย ["$id", "_r", "$state", "$patch", "$subscribe", "$reset", "isLoggedIn"]
Maybe I'm using it wrong, but I thought something like this should work:
// component.vue
import { defineComponent } from '@vue/composition-api';
export default defineComponent({
setup() {
const app = useAppStore();
const loading = app.state.loading;
return { loading };
})
});
// external function
function startLoading() {
const app = useAppStore();
app.state.loading = true;
}
Whenever this external function is called, my component doesn't update based on the state change. Only if I wrap the state
in toRefs
does it update correctly when being changed "from the outside":
import { defineComponent, toRefs } from '@vue/composition-api';
export default defineComponent({
setup() {
const app = useAppStore();
// now it updates correctly when startLoading() is called somewhere else
const loading = toRefs(app.state).loading;
return { loading };
})
});
Am I misunderstanding something here, or is this a bug?
I made a CodeSandbox with an example:
https://codesandbox.io/s/pinia-example-rbb44
I was looking for a way to reset all stores completely in order to write tests for my store, and found that you use setActiveReq({})
in the tests for Pinia itself. Is this the recommended way of testing stores created with Pinia?
The following works exactly as needed, but I'm not sure what setActiveReq
is doing in this case or if I should be using it to begin with.
import Vue from 'vue';
import VueCompositionAPI from '@vue/composition-api';
import { useMainStore } from '@/stores/main';
import { setActiveReq } from 'pinia';
describe('Stores', () => {
beforeAll(() => {
Vue.use(VueCompositionAPI);
});
beforeEach(() => {
setActiveReq({});
});
describe('Actions / reset', () => {
it('Sets counter to 0', () => {
const store = useMainStore();
store.state.counter = 25;
store.reset();
expect(store.state.counter).toBe(0);
});
});
});
Seems to happen at setActiveReq(context.ssrContext.req);
"nuxt": "^2.11.0",
"nuxt-composition-api": "^0.9.0",
"@vue/composition-api": "^0.6.1",
"pinia": "0.0.6",
universal mode
export const useMainStore = defineStore({
id: "main",
state: () => ({
id: 1,
counter: 0,
}),
getters: {
doubleCount() {
//this works
return this.counter * 2;
},
},
actions: {
reset() {
//this no longer works, 'this' is not typed to store instance
this.counter = 0;
}
}
});
I should be able to define any properties with any types in the state.
Most likely it's a collision with 'id' property on store itself which is typed as string. It works as expected in getters just actions are broken.
Hello,
I noticed in the doc one very important point:
There is one important rule for this to work: the useMainStore (or any other useStore function) must be called inside of deferred functions
Further in the doc, when speaking about shared getters, we have this example:
import { computed } from '@vue/composition-api'
import { useUserStore } from './user'
import { useCartStore } from './cart'
export const summary = computed(() => {
const user = useUserStore()
const cart = useCartStore()
return `Hi ${user.state.name}, you have ${cart.state.list.length} items in your cart. It costs ${cart.price}.`
})
I tried it, by importing this shared getter from a component (note: i'm using TS):
import { createComponent } from '@vue/composition-api';
import { summary } from '@/store/shared-getters';
export default createComponent({
name: 'Home',
setup() {
...
return { summary };
}
});
and get an error : Error: [vue-composition-api] must call Vue.use(plugin) before using any function.
I guess it's because as soon as we import summary it gets evaluated and we fall right into the rule above : we must call it inside a deferred function
So what i had to do (but i'm not sure if this is the right way to do it) is to export summary as a function:
export default function summary() {
return computed(() => {
...
});
}
After that it worked properly. If this is intended behavior, i would recommend to document it because it may not be obvious how to implement shared getters based on the current documentation. If this is not intended behavior, i would appreciate any guidance on how to make it work ๐
Cheers
Yannick
Maybe it is my misunderstanding of how stores work and it's by design but currently Vue throws a warning when I use lifecycle hooks and declare them after declaring a store. I would like to declare stores at the beginning of setup() method and then use them within my hooks.
I modified the sandbox to include the warning, if you move the lifecycle hook at the top of setup method it goes away.
https://codesandbox.io/s/pinia-vue-3-demo-forked-85iy9?file=/src/App.vue
Warning thrown:
onBeforeMount is called when there is no active component instance to be associated with. Lifecycle injection APIs can only be used during execution of setup(). If you are using async setup(), make sure to register lifecycle hooks before the first await statement.
Is this behavior ok? If so, am I supposed to instantiate store every time inside hooks?
I would expect using stores with hooks to be without any restrictions.
I have to declare lifecycle hooks before declaring stores as if they were await calls.
First of all thanks for this awesome library.
When I'm using nuxt to build my apps, I normally tend to use Nuxt axios and auth modules for instance and have my API methods wrapped around vuex accessing this.$axios
to perform HTTP requests without being worried about authentication and other configuration stuff.
I want to know if it is possible to have nuxt context injected inside my pinia store so i can keep using this pattern? If so how can I achieve this?
Thanks.
Heya! I've been playing around with Pinia lately, and am really in love with it so far. I do have one question though, the docs state very clearly that you can only call the store compositions from inside the deferred setup
function of Vue components:
There is one important rule for this to work: the useMainStore (or any other useStore function) must be called inside of deferred functions. This is to allow the Vue Composition API plugin to be installed. Never, ever call useStore like this:
import { useMainStore } from '@/stores/main'
// โ Depending on where you do this it will fail
// so just don't do it
const main = useMainStore()
export default defineComponent({
setup() {
return {}
},
})
However, does that mean that it's completely impossible to read the store from other files? For example, my use case would be to read a value from the store in a navigation guard in my Vue Router router.js
file.
In the tests, you seem to use the store without mocking the setup
function, but I might be mistaken:
https://github.com/posva/pinia/blob/master/__tests__/actions.spec.ts#L52
In the example, I kind of dislike the fact that the component can directly call cart.state.rawItems = [];
. Just because I think that can encourage people to modify state in a disorganized manner. Can we get a plugin setting that disallows state being modified from the component (rather than an explicit action)?
According to this twitter discussion
In order to getting more solid store typing, it could be nice to specify the type which will reflect the store. Currently, the state is inferred by the state initialization.
To be honest, it's already possible by this way :
type MainStare = {
counter: number;
name: string;
}
export const useMainStore = createStore<string, MainState>({
id: 'main',
state: () => ({
counter: 0,
name: 'Edimitchel',
}),
// ...
})
As you can see, the current API forced us to specify the first generic type (see here).
My simple request is following : Could it be possible to swap these two lines ?
If accepted, I would create the PR
The result would be like this (with a better usage if we have nested or optional state) :
export type MainStare = {
counter: number;
name: string;
team?: {
name: string;
createdAt?: Date;
}
}
export const useMainStore = createStore<MainState>({
id: 'main',
state: () => ({
counter: 0,
name: 'Edimitchel',
team: {
name: 'Vue Community'
}
}),
// ...
})
I guess, following could be subject to another issue by amending the createStore
signature :
export type MainStare = {
counter: number;
name: string;
team?: {
name: string;
createdAt?: Date;
}
}
export const useMainStore = createStore<MainState>('main', {
state: () => ({
counter: 0,
name: 'Edimitchel',
team: {
name: 'Vue Community'
}
}),
// ...
})
suppose I have multiple travelers with the same state fields Name and Age.
of course, we will not create multiple store files like this:
stores/traveler-1.js
stores/traveler-2.js
stores/traveler-3.js
how can we create a traveler store dynamically with different id?
// stores/traveler.js
import { createStore } from 'pinia'
export const useTravelerStore = function (name) {
return createStore({
id: 'traveler-' + name,
state: () => ({
name: '',
age: ''
})
})
}
and using in Traveler.vue like this
import { useTravelerStore } from './stores/traveler'
export default {
props: {
name: String
},
setup({name}) {
let { state } = useTravelerStore(name)()
return { state }
}
}
and use traveler form as:
<traveler-form name="one"></traveler-form>
<traveler-form name="two"></traveler-form>
<traveler-form name="three"></traveler-form>
is there any better solution or that way is ok?
VueX has an option to subscribeAction
. I think it would be even more relevant here. Pinia currently has subscribe()
but it's just a simple wrapper over watch()
and it often might not be precise enough.
Subscribing to actions would be more explicit.
https://vuex.vuejs.org/api/#subscribeaction
store.subscribeAction((action, state) => {
console.log(action.type)
console.log(action.payload)
})
Usecase:
In many places in the code you might want to react to userStore.state.isLoggedIn
. Current store.subscribe()
may fire multiple times, you'd need to check for truthy value of loggedIn, check if it's different from previous value and so on.
When using Pinia from a fetch function in page with Nuxt, we get the following error for each Pinia store initiated.
WARN: Cannot stringify POJOs with symbolic keys Symbol(vfa.key.reactiveIdentifier)
It doesn't seem to affect the functionality, but this seems to be something that shouldn't happen...
Bug can be reproduced with a clean Nuxt installation:
npx create-nuxt-app piniabug
cd piniabug
yarn add pinia @vue/composition-api
# create a store according to example in Pinia README to stores/main.js
# setup the Nuxt plugin according to Pinia README
# setup composition-api as Nuxt plugin (see below)
# create a fetch function in the pages/index.vue file (see below)
import Vue from 'vue';
import VueCompositionApi from '@vue/composition-api';
Vue.use(VueCompositionApi);
export * from '@vue/composition-api';
import { useMainStore } from '@/stores/main'
...
fetch({ req }) {
const main = useMainStore(req);
console.log(main.state);
},
...
The error goes away if I use for example lodash cloneDeep on the req before passing it in to useMainStore, but that seems to break Pinia somehow, so that store modifications done during the fetch method are not available in the client-side.
Should pinia serialize the state with JSON.stringify?
Hello, I have tried today a new version of Pinia 0.2.0 for Vue 2.
I have noticed an issue with Vue Devtools, they stopped showing state of pinia store, mutations are being displayed however state is not.
(Vue Devtools recently changed default settings for loading state to manual "Load State" button, but that should not be related to this issue)
I have setup reproduction in my repository.
Firstly after pulling a repo try to serve a master branch which has old pinia 0.1.0 which works correctly and displaying the state. (there is a clicking button in UI which increments counter)
git clone https://github.com/dyamon-cz/pinia-bug-store-state.git
cd pinia-bug-store-state
npm ci #must use ci to get data from package-lock.json and do not update packages
npm run serve
After clicking on the "click" button in the UI the state is being properly displayed.
Then switch to bug-devtools branch reinstall node modules and serve. This branch has pinia 0.2.0 set up in main.js based on documentation.
git checkout --track origin/bug-devtools
npm ci #must use ci to get data from package-lock.json and do not update packages
npm run serve
Now there is no state displayed after clicking on the button.
Thank you very much for help!
To completely eliminate the need for Vuex, since Vuex has session persistence support via a plugin.
Accept an extra object in the createStore initialization:
export const usePiniaStore = createStore({
id: "simpleStore",
state: () => ({
simpleVariable: "",
}),
persistence: {
enable: true,
mode: "localSession"
}
});
// type definition
interface IPiniaPersistence {
enable: boolean,
mode: "localSession" | "localStorage";
}
There does not seem to be any Vuex-less alternatives out there that supports the vue-composition-api.
Main issue: nuxt/nuxt#8620
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.