Good Code
The good version models loading, success, and error paths in the same component state. The template shows a user-facing result for each path.
Lesson 07
Model loading, data, and error state together so async views do not render stale or silent failures.
<script setup lang="ts">
import { onMounted, ref } from "vue";
const reviews = ref<Review[]>([]);
const isLoading = ref(false);
const errorMessage = ref("");
// Loading and error refs describe every request state.
async function loadReviews() {
isLoading.value = true;
errorMessage.value = "";
try {
const response = await fetch("/api/reviews");
if (!response.ok) throw new Error("Reviews failed to load");
reviews.value = await response.json();
} catch (error) {
errorMessage.value = "Reviews are unavailable right now.";
} finally {
isLoading.value = false;
}
}
onMounted(loadReviews);
</script>
<template>
<p v-if="isLoading">Loading reviews...</p>
<p v-else-if="errorMessage">{{ errorMessage }}</p>
<ReviewList v-else :reviews="reviews" />
</template><script setup>
import { onMounted, ref } from "vue";
const reviews = ref([]);
// Failed requests disappear into the console.
onMounted(async () => {
reviews.value = await fetch("/api/reviews").then((response) => response.json());
});
</script>
<template>
<ReviewList :reviews="reviews" />
</template>The good version models loading, success, and error paths in the same component state. The template shows a user-facing result for each path.
The bad version assigns data only on success. Failed requests can leave the page empty with no message or retry path.