I am calling an endpoint to bring back an object, which does fetch the data, however not fast enough for the component to grab the data and render. Instead, the component renders with blank values where there should be data.
If I break point the code on creation, then continue maybe a second later, the text correctly renders.
How do I implement it to not render until the data is back?
My API call:
checkScenarioType: function () {
this.$http.get('ScenariosVue/GetScenarioTypeFromParticipant/' + this.ParticipantId).then(response => {
// get body data
this.ScenarioType = response.body.value;
if (this.ScenarioType.timeConstraint) {
store.commit('switchConstraint');
}
}, response => {
// error callback
});
}
The component having the issues:
var questionArea = Vue.component('questionarea', {
props: ["scenariotype"],
data: function () {
return ({
position: "",
vehicleType: ""
});
},
methods: {
transformValuesForDisplay: function () {
switch (this.scenariotype.perspective) {
case 1: {
this.position = "Driver";
this.vehicleType = "Autonomous";
break;
}
case 2: {
this.position = "Passenger";
this.vehicleType = "Manually Driven";
break;
}
case 3: {
this.position = "Driver";
this.vehicleType = "Manually Driven";
break;
}
}
}
},
beforeMount() {
this.transformValuesForDisplay();
},
template:
`<h1>You are the {{ this.position }}! What should the {{ this.vehicleType }} car do?</h1>`
});
React does not wait to render.There is no way to make it wait. All is not lost, though. There's an easy fix. Components that render async data need to be prepared to render an empty state, at least once.
You can use the async/await syntax or call the . then() method on a promise to wait for it to resolve. Inside of functions marked with the async keyword, you can use await to wait for the promises to resolve before continuing to the next line of the function.
To wait for a ReactJS component to finish updating, we use a loading state in our react application by use of the conditional rendering of the component. This can be achieved by the use of the useState and useEffect hooks in the functional components.
With Suspense, you have the ability to suspend component rendering while async data is being loaded. You can pause any state update until the data is ready, and you can add async loading to any component deep in the tree without plumbing all the props and state through your app and hoisting the logic.
There is no way to make it wait. All is not lost, though. There’s an easy fix. Components that render async data need to be prepared to render an empty state, at least once. Think about what your app should look like before the data is ready. Maybe it’s empty, or maybe it’s a loading spinner, or some fancy skeleton state.
Error when using await in render() Why? Because asyncoperator makes the renderfunction to return a Promise, which is an object and React doesn’t know to render objects. So, how do you make React wait for your function before render?
Initializing state actually does run before the first render, and leaving it uninitialized is a common source of problems. This leads to errors like Cannot read property 'map' of undefined' when the component tries to render before the data is ready.
React does not wait to render. Ever. React will gladly kick off an asynchronous data fetch in the background, but then it will immediately proceed with rendering – whether the data has loaded or not. (and you can be almost certain that it will not have loaded yet)
In cases like there's asynchronous loading of data, we typically use a simple v-if
to hide the element until the data is present.
The template would be like:
<h1 v-if="position">You are the {{ position }}! What should the {{ vehicleType }} car do?</h1>
Notice the use of this
in the template is unnecessary.
Also, in your case, instead of the beforeMount()
hook, you would add a (deep/immediate) watch to the prop, to pick up changes when it is loaded externally:
watch: {
scenariotype: {
handler: function(newValue) {
this.transformValuesForDisplay();
},
deep: true,
immediate: true
}
},
Full demo below.
Vue.component('questionarea', {
props: ["scenariotype"],
data: function () {
return ({
position: "",
vehicleType: ""
});
},
methods: {
transformValuesForDisplay: function () {
switch (this.scenariotype.perspective) {
case 1: {
this.position = "Driver";
this.vehicleType = "Autonomous";
break;
}
case 2: {
this.position = "Passenger";
this.vehicleType = "Manually Driven";
break;
}
case 3: {
this.position = "Driver";
this.vehicleType = "Manually Driven";
break;
}
}
}
},
watch: {
scenariotype: {
handler: function(newValue) {
this.transformValuesForDisplay();
},
deep: true,
immediate: true
}
},
template:
`<h1 v-if="position">You are the {{ position }}! What should the {{ vehicleType }} car do?</h1>`
});
new Vue({
el: '#app',
data: {
ScenarioType: {perspective: null}
},
methods: {
checkScenarioType: function () {
this.$http.get('https://reqres.in/api/users/2').then(response => {
// get body data
this.ScenarioType.perspective = response.body.data.id; // for testing purposes only
}, response => {
// error callback
});
}
},
mounted: function() {
this.checkScenarioType();
}
})
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vue-resource"></script>
<div id="app">
<p>Notice while it is null, the h1 is hidden: {{ ScenarioType }}</p>
<br>
<questionarea :scenariotype="ScenarioType"></questionarea>
</div>
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