I have a Vuex state that holds a user ID. In my component's mounted(), I try to use that user ID, but it's always null.
How do I get the state from my computed mapGetters into my mounted()?
Here's my computed:
computed: {
...mapGetters('auth', [
'userid'
])
}
And here's my mounted():
mounted () {
HTTP.get('account/' + this.userid + '/')
.then((response) => {
this.account = response.data
})
}
The this.userid is always null.
BTW when I look at the Vue inspector, the auth/userid has the correct value in the getter auth/userid. How do I access auth.userid from mounted()?
userid might not be available at the time component is mounted. You can fix it by watching userid value, and only call HTTP request when userid is changed and available:
computed: {
...mapGetters('auth', [
'userid'
])
},
watch: {
'userid': {
handler (newVal) {
if (newVal) { // check if userid is available
this.getAccountInformation()
}
},
immediate: true // make this watch function is called when component created
}
},
methods: {
getAccountInformation () {
HTTP.get('account/' + this.userid + '/')
.then((response) => {
this.account = response.data
})
}
}
DEBUG
To debug this, first skip the mapGetters, and even getters, and return your state directly.
For example.
computed:{
userId() { return this.$store.state.auth.userid }
}
I don't know how your store or modules are set up, so you might have to change things a bit.
Once that works, add it to your getters and use this.$store.getters.userid, or such.
Finally, when that works, try your original mapGetters and double check your module alias.
POSSIBLE ASYNC ISSUE
Now, on the other hand, if your getter is async, you will also get a null, before the userid promise resolves. You would have to use an asyncComputed, or wait for the result in your mounted.
Related
I dont know why, but my computed property is not firing when state changes.
I am dispatching actions and as a result state changes. but data is not firing at all. wanna help :(
//store
mutations: {
setStudyData(state, payload) {
state.studyData = [...payload];
},
},
actions: {
async postLogin({ state, commit, dispatch }, { userId, userPass }) {
try {
const res = await axios.post(`${url}/v1/api/user/auth/signin`, {
userId,
userPass,
});
await commit("setSeq", res.data.user_seq);
await commit("setToken", res.data.accessToken);
await dispatch("getStudyLi");
console.log(state.studyData); //i can see the state changes here
} catch {
console.log("error");
}
},
computed: {
data(){
console.log(this.datas) //not working
return this.$store.state.login.studyData
}
},
You don't have any this.datas property in the code shown.
It is also important how do use the computed. It runs the computed method only if there is some call of computed value. You can not expect method to be called if you do not use this.data property in your component.
U can just use mapState instead, if u not gonna process your studyData anymore and save your time writing this.$store.state.login.studyData
computed: {
...mapState([
"studyData",
"studyData2",
]),
}
I am building a web app with nuxt.
here's simplified code:
pages/index.vue
data() {
return {
item: {name:'', department: '', testField: '',},
}
}
async asyncData() {
const result = call some API
const dataToInitialize = {
name: result.username,
department: result.department,
testField: //want to assign computed value
}
return {item: dataToInitialize}
}
Inside asyncData, I call API and assign value to dataToInitialize.
dataToInitialize has testField field, and I want to assign some computed value based on username and department.
(for example, 'a' if name starts with 'a' and department is 'management'..etc there's more complicated logic in real scenario)
I have tried to use computed property , but I realized that asyncData cannnot access computed.
Does anyone know how to solve this?
Any help would be appreciated!
=======
not sure if it's right way, but I solved the issue by setting 'testfield' inside created.
created() {
this.item.testField = this.someMethod(this.item);
},
Looking at the Nuxt lifecyle, you can see that asyncData is called before even a Vue instance is mounted on your page.
Meanwhile, fetch() hook is called after. This is non-blocking but more flexible in a lot of ways.
An alternative using fetch() would look like this
<script>
export default {
data() {
return {
staticVariable: 'google',
}
},
async fetch() {
await this.$axios(this.computedVariable)
},
computed: {
computedVariable() {
return `www.${this.staticVariable}.com`
},
},
}
</script>
Another alternative, would be to use URL query string or params, thanks to Vue-router and use those to build your API call (in an asyncData hook).
Here is an example on how to achieve this: https://stackoverflow.com/a/68112290/8816585
EDIT after comment question
You can totally use a computed inside of a fetch() hook indeed. Here is an example on how to achieve this
<script>
export default {
data() {
return {
test: 'test',
}
},
async fetch() {
const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${this.nice}`)
console.log(await response.json())
},
computed: {
nice() {
return this.test + 'wow!'
},
},
}
</script>
I found that destructuring fetch({}) causes issues with accessing this inside fetch scope ->
async fetch({ store, $anyOtherGlobalVar }){
store.dispatch...
// destructuring approach changes the scope of the function and `this` does not have access to data, computed and e.t.c
}
If you want to access this scope for example this.data, avoid destructuring and access everything through this.
async fetch() {
this.$store...
this.data...
}
How to fix it?
computed: {
...mapGetters({
eleron: 'promoter/eleron',
}),
},
GetInfo (call when press search button):
getInfo() {
this.loading = true;
axios.post('/srt', {
search: this.search
})
.then((response) => {this.eleron = response.data, console.log(response.data), this.loading = false;});
},
You are mapping the getters from vuex. This means that you can only get the value from the store, you cannot write to it.
You need to also map a mutation.
Something like this should work, depending on the fact that you have a mutation defined on the store:
methods: {
...mapMutations([
'updateEleron'
]),
}
And then call it in the promise callback
this.updateEleron(response.data)
Note: vuex offers read only access to variables from outside the store. Writing to a variable needs to be done from inside a mutation or action.
In my parent component:
<UsersList :current-room="current_room" />
In the child component:
export default {
props: {
currentRoom: Object
},
data () {
return {
users: []
}
},
mounted () {
this.$nextTick( async () => {
console.log(this.currentRoom) // this, weirdly, has the data I expect, and id is set to 1
let url = `${process.env.VUE_APP_API_URL}/chat_room/${this.currentRoom.id}/users`
console.log(url) // the result: /api/chat_room/undefined/users
let response = await this.axios.get(url)
this.users = response.data
})
},
}
When I look at the page using vue-devtools, I can see the data appears:
I've run into this issue in the past – as have many others. For whatever reason, you can't rely on props being available in the component's mounted handler. I think it has to do with the point at which mounted() is called within Vue's lifecycle.
I solved my problem by watching the prop and moving my logic from mounted to the watch handler. In your case, you could watch the currentRoom property, and make your api call in the handler:
export default {
props: {
currentRoom: Object
},
data() {
return {
users: []
}
},
watch: {
currentRoom(room) {
this.$nextTick(async() => {
let url = `${process.env.VUE_APP_API_URL}/chat_room/${room.id}/users`
let response = await this.axios.get(url)
this.users = response.data
})
}
},
}
I don't think you really need to use $nextTick() here, but I left it as you had it. You could try taking that out to simplify the code.
By the way, the reason console.log(this.currentRoom); shows you the room ID is because when you pass an object to console.log(), it binds to that object until it is read. So even though the room ID is not available when console.log() is called, it becomes available before you see the result in the console.
How can i destroy this watcher? I need it only one time in my child component, when my async data has loaded from the parent component.
export default {
...
watch: {
data: function(){
this.sortBy();
},
},
...
}
gregor ;)
If you construct a watcher dynamically by calling vm.$watch function, it returns a function that may be called at a later point in time to disable (remove) that particular watcher.
Don't put the watcher statically in the component, as in your code, but do something like:
created() {
var unwatch = this.$watch(....)
// now the watcher is watching and you can disable it
// by calling unwatch() somewhere else;
// you can store the unwatch function to a variable in the data
// or whatever suits you best
}
More thorough explanation may be found from here: https://codingexplained.com/coding/front-end/vue-js/adding-removing-watchers-dynamically
Here is an example:
<script>
export default {
data() {
return {
employee: {
teams: []
},
employeeTeamsWatcher: null,
};
},
created() {
this.employeeTeamsWatcher = this.$watch('employee.teams', (newVal, oldVal) => {
this.setActiveTeamTabName();
});
},
methods: {
setActiveTeamTabName() {
if (this.employee.teams.length) {
// once you got your desired condition satisfied then unwatch by calling:
this.employeeTeamsWatcher();
}
},
},
};
</script>
If you are using vue2 using the composition-api plugin or vue3, you can use WatchStopHandle which is returned by watch e.g.:
const x = ref(0);
setInterval(() => {
x.value++;
}, 1000);
const unwatch = watch(
() => x.value,
() => {
console.log(x.value);
x.value++;
// stop watch:
if (x.value > 3) unwatch();
}
);
For this kind of stuff, you can investigate the type declaration of the API, which is very helpful, just hover the mouse on it, and it will show you a hint about what you can do: