I have a use case with the pinia in vue 3 that I want to dynamically add new entries to the pinia store using the store actions. for example if I have a state called firstName and if I call a the action of the store it should add new state called lastName in the state as well. Here is what I have tried
import { defineStore } from "pinia";
export const useAdvanceListingsFilterStore = defineStore(
"advance-listing-filters",
{
state: () => {
return {
firstName: "jhon",
};
},
actions: {
setLastName(payload) {
return {
...this.state,
lastName: payload,
};
},
},
}
);
The new state should include the fistName and lastName fields.
UPDATE
Accessing user over $state
was not necessary. It works well directly:
this.user.lastName = payload;
const { ref, createApp } = VueDemi
const { createPinia, defineStore, storeToRefs } = Pinia
const useAlertsStore = defineStore("advance-listing-filters",
{
state: () => ({ user: { firstName: "John" }}),
actions: {
setLastName(payload) {
this.user.lastName = payload;
}
}
}
)
const App = {
setup() {
const store = useAlertsStore()
const { user } = storeToRefs(store)
const lastName = ref('Doe')
return {
store,
user,
lastName
}
}
}
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.mount('#app')
[v-cloak] { display: none; }
<div id="app" v-cloak>
<label>Name: </label> <b>{{store.user.firstName}} {{store.user.lastName}}</b><br /><br />
<label>First Name:</label> <input :value="user.firstName" disabled /><br /><br />
<label>Last Name:</label> <input v-model="lastName" /><br /><br />
<button @click="store.setLastName(lastName)">Set Last Name</button>
<button @click="store.$reset()">Reset</button>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/[email protected]/lib/index.iife.js"></script>
<script src="https://unpkg.com/[email protected]/dist/pinia.iife.js"></script>
The simplest way would by add lastName: null
to your state, but I guess it is not what you are trying to achieve.
I have tried to add new items to the state internally using state.$path
(see Mutating the state), but the new lastName
item was still not accessible outside the state using state.lastName
. So I haven't found a way to achieve your goal directly. But there is another way to do it.
You can use a nested object to achieve your goal.
See the playground below.
I should also state, that adding state items dynamically makes your application less predictable.
So, I would rather rethink the design and flow of data.
const { ref, createApp, defineComponent } = Vue
const { createPinia, defineStore, storeToRefs } = Pinia
const useAlertsStore = defineStore("advance-listing-filters",
{
state: () => {
return {
user: { firstName: "John" }
};
},
actions: {
setLastName(payload) {
this.$state.user.lastName = payload;
}
}
}
)
const App = {
setup() {
const store = useAlertsStore()
const { user } = storeToRefs(store)
const lastName = ref('Doe')
return {
store,
user,
lastName
}
}
}
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.mount('#app')
<div id="app">
<label>Name: </label> <b>{{store.user.firstName}} {{store.user.lastName}}</b><br /><br />
<label>First Name:</label> <input :value="user.firstName" disabled /><br /><br />
<label>Last Name:</label> <input v-model="lastName" /><br /><br />
<button @click="store.setLastName(lastName)">Set Last Name</button>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/[email protected]/lib/index.iife.js"></script>
<script src="https://unpkg.com/[email protected]/dist/pinia.iife.js"></script>