I am working on my first Vue project. I'm used to React and vanilla js, but just getting my head around a few concepts in Vue here.
In particular, importing state and action props from a Pinia store, and seemingly having to import those multiple times in a single Vue component (something I don't need to do in React).
In this example, I am importing a simple count value, and an increment function, and trying to use these in a few different places:
<script setup>
// I import everything initially in setup, which works fine,
// and these props (currentCount and incrementCount)
// can be used in my template:
import { storeToRefs } from 'pinia';
import { useStore } from '@/stores/store';
const { currentCount } = storeToRefs(useStore());
const { incrementCount } = useStore();
</script>
<template>
<main>
Current count: {{ currentCount }}
<button @click="incrementCount">Increment</button>
</main>
</template>
<script>
// I can't use store values from setup here.
// This doesn't work:
// console.log(currentCount);
// I also can't import store values here.
// I get the following error:
// "getActivePinia was called with no active Pinia"
// const { currentCount } = storeToRefs(useStore());
export default {
mounted() {
// I have to import store values here for them to work:
const { currentCount } = storeToRefs(useStore());
console.log(currentCount);
},
watch: {
// weirdly, this reference to watching "currentCount" works:
currentCount() {
// I also have to import store values here for them to work:
const { currentCount } = storeToRefs(useStore());
console.log(currentCount);
},
},
};
</script>
As you can see, if I want to use store values in my template, on mount, and in a watcher (whereby I'd use React's useEffect hook) I am having to import the store props 3 times in total.
Is this correct / normal? Is there a simpler way to achieve what I'm doing, where I only import props once? I want to be sure I haven't missed something and am not doing something in an unusual way.
Thanks for any help and advice!
CodePudding user response:
Pinia was designed with Composition API in mind. Which implies instead of the Options API mounted
, watch
, data
, etc... you move everything in setup
function and you use onMounted
and watch
imported directly from vue
(or from @vue/composition-api
if you're still on Vue2):
in your case:
import { defineComponent, onMounted, watch, toRefs } from '@vue/composition-api';
// or from 'vue' in Vue3
export default defineComponent({
setup() {
const store = useStore();
onMounted(() => {
console.log(store.currentCount);
});
watch(
() => store.currentBrightness,
(newVal, oldVal) => {
console.log('currentBrightness', { newVal, oldVal })
}
);
return {
...toRefs(myStore)
}
}
})
If you use Options API, you'd need to call useStore()
inside each of the methods
, computed
or life-cycle hooks where you need to use it.
Alternatively, in Options API you could use the mapState
helper from Pinia and then you could use this.currentBrightness
or this.currentCount
directly.
e.g:
import { mapState } from 'pinia'
// ...
computed: {
...mapState(useStore, ['currentBrightness', 'currentCount'])
}
// you can now use `this.currentBrightness` in any hook/method/computed
CodePudding user response:
If you want to combine pinia
stores with the options API, one way to do it is to use the setup()
function inside the options to call useStore
:
<script>
import { useStore } from '@/stores/store';
export default {
setup() {
const store = useStore();
return {store}
},
watch: {
store.currentBrightness(newVal, oldVal){
// your code
}
},
methods: {
// inside methods use this.store
},
mounted() {
console.log(this.store.currentCount);
}
}
</script>
Some might consider this as a unwanted mix of composition and options API, but in my view it is a quite good solution for pinia stores.