Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

vue3 + pinia: how to make reactive a value from the store

I am using vue 3 with composition api and pinia

I have an auth store that is reading from the store a default email and a default password

import { useAuthStore } from "stores/auth";
const authStore = useAuthStore();

const email = authStore.loginUser;
const password = authStore.passwordUser;

Then I am using email and password as v-model.

The problem is that both are not reactive. If I change the value from the text input, the model is not updated

I ask kindly for an explanation of the problem and a solution.

like image 646
realtebo Avatar asked Jan 23 '26 17:01

realtebo


2 Answers

// ❌ loses reactivity
const email = authStore.loginUser 

creates an email constant with the current value of authStore.loginUser, losing reactivity. To keep reactivity, you could use computed:

import { computed } from 'vue'

// ✅ keeps reactivity
const email = computed({
  get() { return authStore.loginUser },
  set(val) { authStore.loginUser = val }
})
// email is now a computed ref

...or you could use the provided storeToRefs wrapper, designed for extracting/deconstructing store reactive props while keeping their reactivity (basically to avoid the above boilerplate):

import { storeToRefs } from 'pinia'
// ✅ keeps reactivity
const { 
  loginUser: email,
  passwordUser: password
} = storeToRefs(authStore)
// email & password are now refs

Important: you only want to deconstruct state and getters using storeToRefs. Actions should be used directly from the store object (authStore in your case) or deconstructed without the wrapper:

const { actionOne, actionTwo } = authStore

This is specified in docs linked above:

... so methods and non reactive properties are completely ignored.


In conclusion, you typically end up with two deconstructions from each store:

import { useSomeStore } from '@/store'
// reactive:
const { s1, s2, g1, g2 } = storeToRefs(useSomeStore())
// non-reactive:
const { a1, a2 } = useSomeStore()

where s1, s2 are state members, g1, g2 are getters and a1, a2 are actions.

like image 140
tao Avatar answered Jan 25 '26 23:01

tao


This worked for me: authStore.js:

import { defineStore } from "pinia";
import { ref } from "vue";

export const useAuthStore = defineStore("authStore", {
  state: () => ({
    email: ref("[email protected]"),
    password: ref("secret"),
  }),
});

And then a Login.vue component:

<script setup>
import { useAuthStore } from "../stores/authStore.js";
const store = useAuthStore();
</script>

<template>
  <form>
    <input type="email" v-model="store.email" />
    <input type="password" v-model="store.password" />
  </form>
</template>
<style>
input {
  display: block;
  border: 1px solid black;
  margin: 3px;
}
</style>

Which seems simpler than the accepted answer.

like image 26
Niels Bom Avatar answered Jan 25 '26 21:01

Niels Bom



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!