I keep running into an issue where I am awaiting data from a prop to do something within it inside my created hook. It keeps throwing errors such as "this.value is not defined" (As it has not yet loaded from prop)
I have fixed some of these issues using v-if
and only using as needed. However in some cases I wanted to use that prop info within my created and when that happens, I end up getting errors and am not sure how to best tell that part of the created to "wait" for the data before running.
Any advice?
--
Update to add some example code.
On the App.js page, I am calling in the "userInfo" and setting it in data like so. I am doing this for two reasons. First to check if user is logged in, and secondly taking the time to save the users info as it will be used later on throughout the app. (Please note, user and userInfo are different and contain different info. User is email/password whilst userInfo is username, bio, etc.)
data() {
return {
user: null,
userInfo: null,
}
},
created(){
//let user = firebase.auth().currentUser
firebase.auth().onAuthStateChanged((user) => {
if(user){
this.user = user
let ref = db.collection('users')
.where('user_id', '==', this.user.uid)
ref.get().then(snapshot => {
snapshot.forEach(doc => {
this.userInfo = doc.data()
})
})
} else {
this.user = null
}
})
},
Now, I pass this information down within the using v-bind. and then call it in areas I need some info using props.
The issue comes when I want to do something such as -
if(this.userInfo) {
value = true
}
Calling this.userInfo throws an error "this.userInfo is null" (I set it to null in the data() of the page as default).
As already mentioned in the comments, you're running into a Promise resolving issue. When passing a result as a Prop which gets resolved by a Promise, that result will basically be empty which eventually will lead to an error in your Child Components created()
hook.
I would recommend using v-if
as you've already done in the Child Component itself. However, I'd use the v-if
in the Parent Component to simply show/hide the Child. That should not only resolve the problem within your created()
hook, but also help building up a more readable markup in your Child Component where you could just remove all v-if
s.
So let's assume you have a Parent/Child relationship like that.
<SomeParentComponent>
<SomeChildComponent :someProp="yourProp" />
</SomeParentComponent>
When yourProp
is set after a resolving Promise, the initial value will be null
, undefined
, or whatever you've set it to. To avoid passing in an empty prop you can just wrap the in a v-if
which's expression evaluates to yourProp
just like so.
<SomeParentComponent>
<SomeChildComponent :someProp="yourProp" v-if="yourProp" />
</SomeParentComponent> <!-- ^-- See here -->
Doing so, the Child will only be rendered when yourProp
is set and therefore you won't get any errors anymore.
The accepted answer provides a great solution, which set v-if
on entire Child component in Parent component. However there's still some edge cases where your prop literally becomes empty or null if the API returns so, which eventually ends up never rendering your Child component at all though you might need to show some default texts/styles, or maybe you're working on some projects that you don't have control over the parent component. In those cases, you could internally handle this problem just fine by checking if the prop has already available in the created hook, if not, you should dynamically watch this prop until it becomes available.
// your component
export default {
props: ['userInfo'],
created() {
// check if the prop userInfo available or not
if (this.userInfo) {
// do your thing
} else {
// start watching this prop
const unwatch = this.$watch('userInfo', () => {
// do your thing when userInfo is available
// tear down this watcher as you now no longer need it
unwatch()
})
}
}
}
Props should be available in the created
hook. Assuming the values are synchronous. Try to add default
values, in order to remove unnecessary v-if
directives. This should solve the issue for both synchronous and asynchronous data.
{
props: {
length: {
type: Number,
default: 0,
},
},
}
There are at least two approaches I can think of:
The parent/root to provide
the data/object down the component tree and the receiving end (children) to inject
them.
Parent
{
data: () => ({
userInfo: null
}),
created() {
// Populates data via Firebase
// Code omitted for brevity...
this.userInfo = doc.data();
},
provide() {
return Object.defineProperty({}, 'userInfo', {
get: () => this.userInfo
})
}
}
Children
// Children
{
inject: ['userInfo'],
created() {
if (this.userInfo) {
// Do something
}
},
mounted() {
if (this.userInfo) {
// Do something
}
},
// Any lifecycle hooks, methods or virtually anywhere needing a reference to this object
}
Rather than passing props and having to deal with possible asynchrony issues; you could let Vuex take care of synchronizing the states for you and stop worrying about the data being null
or undefined
.
Store
export default {
state: {
userInfo: null
},
mutations: {
UPDATE_USER(state, payload) {
state.userInfo = payload;
}
}
}
Parent/App.js
export default {
created() {
// On firebase success callback
this.$store.commit('UPDATE_USER', doc.data());
}
}
Children
import { mapState } from 'vuex';
export default {
computed: {
...mapState([
'userInfo'
])
},
created() { },
mounted() { },
methods() {
if (this.userInfo) {
// Do something safely
}
},
// Anywhere
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With