In Vue, what if I need to use state from getters while mounted life cycle hook? - vue.js

I try to use data from vuex during mounted lifecycle hook.
However, it seems like mounted life cycle hook is excuted before I get the data from vuex.
How do I access the data from vuex and use it during mounted life cycle hook?
The code is as below.
I bring data by getters like this.
computed:{
targetCounty(){
return this.$store.getters['parkingModule/byCounty'][this.countyname]
}
Then I need to feed this data to my class constructur by init() method
init(){
scene =new THREE.Scene();
const canvas=document.querySelector('#myCanvas');
canvas.width=innerWidth;
canvas.height=innerHeight;
camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1,
1000 );
renderer=new THREE.WebGLRenderer({canvas:canvas})
renderer.setSize( window.innerWidth, window.innerHeight );
let texture = new THREE.TextureLoader().load('disc.png');
let rawRad = this.rawRadius
console.log(this.targetCounty)
const meshobject =new
ParkingSpot(rawRad,this.targetCounty.length,100,texture,this.targetCounty)
sphereMesh= meshobject.createMesh();
camera.position.z = 5
scene.add(sphereMesh);
console.log(sphereMesh.material.size)
},
this init() method is invoked during mounted life cycle hook like this.
mounted(){
this.init()
this.animate();
// window.addEventListener()
},
created(){
console.log(this.targetCounty)
// setTimeout(()=>{console.log(this.targetCounty[0])},3000)
},
However, when I log this.targetCounty, it returns empty array. So I got around it
by rendering computed property in DOM cause computed property runs only the element is rendered.
<template>
<div>
<canvas id="myCanvas"></canvas>
</div>
<p v-show='false'>{{targetCounty}}</p>
</template>
I created dummy DOM element only to get the computed property for my mounted life cycle(I think it's very bad approach)
What would be the solution for solving this problem?

You could use vm.$watch() in the mounted() hook to observe the store's getter for the initial value:
export default {
mounted() {
const unwatch = this.$watch(
() => this.$store.getters['parkingModule/byCounty'][this.countyname],
targetCounty => {
if (targetCounty) {
// handle initial value here...
this.targetCounty = targetCounty
this.init()
this.animate()
unwatch()
}
}
)
}
}
demo

Why don't you try making a function that explicitly returns the value and then invoke it in the mounted() lifecycle hook, saving it into a constant. Then pass that constant into your init function.
const targetCountry = this.$store.getters['parkingModule/byCounty'[this.countyname]
this.init(targetCountry)

Related

How to watch for vuex state?

I need do fire a function within component when my vuex state data change, but it does not work , is there any wrong usage about watch hook for vuex?
const state = {
currentQueryParameter:[],
};
const mutations = {
currentQueryParameter(state,info){
state.currentQueryParameter[info.index]=info.value
Vue.set(info, info.index, info.value);
}
}
in component
watch: {
'$store.state.currentQueryParameter': function() {
console.log("changed")
this.getData()
}
},
What you are doing is technically correct and will work.
However several thing are still going wrong:
If you want your state to be reactive you need to fill Arrays with native array methods (.push(), .splice() etc). Vue.set() is only used to set Object properties.
You are watching currentQueryParameter, which is an Array. Its value does not change through your mutation - it stays the same Array. If you want to watch nested elements as well, you need to use the deep flag in your watcher, like so:
watch: {
'$store.state.currentQueryParameter': {
deep: true,
handler(newVal) {
console.log('queryParameter changed');
}
}
}
I don't know what you are trying to do with this one in your mutation:
Vue.set(info, info.index, info.value); but you should not mutate the properties you pass to a function.

How to get data from vuex state into local data for manipulation

I'm having trouble understanding how to interact with my local state from my vuex state. I have an array with multiple items inside of it that is stored in vuex state. I'm trying to get that data from my vuex state into my components local state. I have no problems fetching the data with a getter and computed property but I cannot get the same data from the computed property into local state to manipulate it. My end goal is to build pagination on this component.
I can get the data using a getters and computed properties. I feel like I should be using a lifecycle hook somewhere.
Retrieving Data
App.vue:
I'm attempting to pull the data before any components load. This seems to have no effect versus having a created lifecycle hook on the component itself.
export default {
name: "App",
components: {},
data() {
return {
//
};
},
mounted() {
this.$store.dispatch("retrieveSnippets");
}
};
State:
This is a module store/modules/snippets.js
const state = {
snippets: []
}
const mutations = {
SET_SNIPPETS(state, payload) {
state.snippets = payload;
},
}
const actions = {
retrieveSnippets(context) {
const userId = firebase.auth().currentUser.uid;
db.collection("projects")
.where("person", "==", userId)
.orderBy("title", "desc")
.onSnapshot(snap => {
let tempSnippets = [];
snap.forEach(doc => {
tempSnippets.push({
id: doc.id,
title: doc.data().title,
description: doc.data().description,
code: doc.data().code,
person: doc.data().person
});
});
context.commit("SET_SNIPPETS", tempSnippets);
});
}
}
const getters = {
getCurrentSnippet(state) {
return state.snippet;
},
Inside Component
data() {
return {
visibleSnippets: [],
}
}
computed: {
stateSnippets() {
return this.$store.getters.allSnippets;
}
}
HTML:
you can see that i'm looping through the array that is returned by stateSnippets in my html because the computed property is bound. If i remove this and try to loop through my local state, the computed property doesn't work anymore.
<v-flex xs4 md4 lg4>
<v-card v-for="snippet in stateSnippets" :key="snippet.id">
<v-card-title v-on:click="snippetDetail(snippet)">{{ snippet.title }}</v-card-title>
</v-card>
</v-flex>
My goal would be to get the array that is returned from stateSnippets into the local data property of visibleSnippets. This would allow me to build pagination and manipulate this potentially very long array into something shorter.
You can get the state into your template in many ways, and all will be reactive.
Directly In Template
<div>{{$store.state.myValue}}</div>
<div v-html='$store.state.myValue'></div>
Using computed
<div>{{myValue}}</div>
computed: {
myValue() { return this.$store.state.myValue }
}
Using the Vuex mapState helper
<div>{{myValue}}</div>
computed: {
...mapState(['myValue'])
}
You can also use getters instead of accessing the state directly.
The de-facto approach is to use mapGetters and mapState, and then access the Vuex data using the local component.
Using Composition API
<div>{{myValue}}</div>
setup() {
// You can also get state directly instead of relying on instance.
const currentInstance = getCurrentInstance()
const myValue = computed(()=>{
// Access state directly or use getter
return currentInstance.proxy.$store.state.myValue
})
// If not using Vue3 <script setup>
return {
myValue
}
}
I guess you are getting how Flux/Vuex works completely wrong. Flux and its implementation in Vuex is one way flow. So your component gets data from store via mapState or mapGetters. This is one way so then you dispatch actions form within the component that in the end commit. Commits are the only way of modifying the store state. After store state has changed, your component will immediately react to its changes with latest data in the state.
Note: if you only want the first 5 elements you just need to slice the data from the store. You can do it in 2 different ways:
1 - Create a getter.
getters: {
firstFiveSnipets: state => {
return state.snipets.slice(0, 5);
}
}
2 - Create a computed property from the mapState.
computed: {
...mapState(['allSnipets']),
firstFiveSnipets() {
return this.allSnipets.slice(0, 5);
}
}

Vue Test Utils - Skip created hook

I want to skip all of the methods that are being called within the created() hook. Is there a way to do this?
So instead of this
created() {
this.getAllocations();
this.getModels();
this.getTeams();
this.getCustodians();
this.getDefaultFeeStructure();
}
I want this
created() { }
It's worth noting, I cannot actually change the component itself, but for testing purposes, this needs to be done.
You can accomplish this with a global mixin (see https://v2.vuejs.org/v2/guide/mixins.html#Global-Mixin)
However, for your case you need a custom merge strategy to prevent the created hook on the component from being run:
Hook functions with the same name are merged into an array so that all of them will be called. Mixin hooks will be called before the component’s own hooks. (https://v2.vuejs.org/v2/guide/mixins.html#Option-Merging)
See a working example at https://jsfiddle.net/rushimusmaximus/9akf641z/3/
Vue.mixin({
created() {
console.log("created() in global mixin")
}
});
const mergeCreatedStrategy = Vue.config.optionMergeStrategies.created;
Vue.config.optionMergeStrategies.created = (parent, child) => {
return mergeCreatedStrategy(parent);
};
new Vue ({
el: "#vue-app",
template: '<p>See console output for logging. Rendered at {{renderDate}}</p>',
data() {
return {
renderDate: new Date()
}
},
created() {
console.log("created() in component")
}
})

Vue sharing state between sibling components

I probably do not want to use vuex for state management yet as it is probably overkill for now.
I took a look at https://v2.vuejs.org/v2/guide/components.html#Non-Parent-Child-Communication. I am using single file component so I am not sure where do I define the shared bus such that both components will have reference to it.
var bus = new Vue()
ChildA.Vue
watch: {
sideNav: (newValue) => {
bus.$emit('sideNav-changed', newValue)
}
}
ChildB.Vue
created () {
bus.$on('sideNav-changed', data => {
console.log(`received new value: ${data}`)
// update own copy here
})
}
Parent.Vue
<template>
<childA>
</childA>
<childB>
</childB>
</template>
I want to watch any changes of sideNav on ChildA and emit it to ChildB to update its own value.
Found the answer to it...
I declare it on the main.js
const bus = new Vue() // Single event hub
// Distribute to components using global mixin
Vue.mixin({
data: function () {
return {
bus : bus
}
}
})
And also change
watch: {
sideNav: (newValue) => {
bus.$emit('sideNav-changed', newValue)
}
}
to
watch: {
sideNav: function (newValue) {
bus.$emit('sideNav-changed', newValue)
}
}
Is this answer any good to you? You can do everything with events, but if you can avoid them, you should. You might not want vuex for now. That's where I am. But you want, right from the start, a store in global scope and reactive pipes. You "declare" the relationship between an element on the page and an item in the store, then basta. Vue takes care of the rest. You don't care about events.
The simplest way to do this would be to just attach it to the window i.e.
window.bus = new Vue()
Then it will be available in all of your components without the need to define a global mixin e.g. this will still work:
watch: {
sideNav(newValue) {
bus.$emit('sideNav-changed', newValue)
}
}

vue computed : add new computed value from created lifecycle hook

I'm beginner in vue
I'm trying to push a computed data with name , that name come from vuex which comes after creating the instance
How can i push new computed property to the instance in the created() hook ??
Here is the code
computed: {
// 3 - i want to extract the object properties here computed
// that is easy bu using spread operator ...
// Problem : vue intialize computed before the created() hook
// so the spreed work befor passing the filling the object
...this.mapGettersObj
},
created(){
// 1- i can access only this line after creating the object
this.stocks = this.$store.state
let arr=[]
for (let stock in this.stocks){
arr.push(stock+'Getter')
}
// 2 - mapGetters returns an object
this.mapGettersObj=mapGetters(arr)
}
If I can create new computed value after creating that will solve the problem
You can do this in beforeCreate hook。
beforeCreate: function() {
this.$options.computed = {
demo2: function() {
return this.demo
}
}
}
I don't know why you are doing what you do, but if you want to have a variable available before the computed is called you can use beforeCreate hook: https://alligator.io/vuejs/component-lifecycle/
You could also do something in the lines of
computed: {
...function() {
this.stocks = this.$store.state
let arr=[]
for (let stock in this.stocks){
arr.push(stock+'Getter')
}
return mapGetters(arr);
}();
},