Vue prop is undefined for a moment when page is reloaded

21 views Asked by At

I have a parent component that retrieves a todo item from a pinia store, based on the current vue router params /todo/123, and passes it into a child component as a prop.

The app works properly when navigating to the component using router-links. Except when I refresh the page while I am in a TodoViewComponent page (localhost:5173/todo/123). When I refresh the page, the props are undefined initially, hence throwing me an error.

Proxy(Object) {todo: undefined}
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'id')
Proxy(Object) {todo: Proxy(Object)}

What would be the solution so that the SingleTodoComponent receives the todo prop "instantaneously" upon page reload?

TodoViewComponent (parent):

<script setup>
import { useRoute } from 'vue-router'
import { computed } from 'vue';

import SingleTodoComponent from '@/components/SingleTodoComponent.vue';
import { useTodos } from '@/stores/todosStore';

const route = useRoute()
const todosStore = useTodos()

const todo = computed(() => todosStore.todos.find(t => {
    return t.id === parseInt(route.params.todoId)
}))
</script>

<template>
            <SingleTodoComponent :todo="todo"></SingleTodoComponent>
</template>

SingleTodoComponent:

<script setup>
const props = defineProps(['todo'])

console.log(props)

const formState = reactive({
    id: props.todo.id,
    content: props.todo.content_en,
    bookmarked: props.todo.bookmarked,
    completed: props.todo.completed,
    date_deadline: props.todo.date_deadline
})
</script>
1

There are 1 answers

1
Samball On

Instead of directly passing todo as a prop, you can pass a loading state as a prop until the todo is fetched from the store.

TodoViewComponent:

<script setup>
import { useRoute } from 'vue-router';
import { computed, ref, onMounted } from 'vue';

import SingleTodoComponent from '@/components/SingleTodoComponent.vue';
import { useTodos } from '@/stores/todosStore';

const route = useRoute();
const todosStore = useTodos();

const loading = ref(true);

const todo = computed(() => todosStore.todos.find(t => {
    return t.id === parseInt(route.params.todoId)
}));

onMounted(() => {
    // Once the component is mounted, set loading to false
    loading.value = false;
});
</script>

<template>
    <SingleTodoComponent :todo="todo" :loading="loading"></SingleTodoComponent>
</template>

SingleTodoComponent:

<script setup>
import { defineProps, ref, onMounted, reactive } from "vue";

const props = defineProps(['todo', 'loading']);

const formState = ref({
    id: null,
    content: '',
    bookmarked: false,
    completed: false,
    date_deadline: ''
});

onMounted(() => {
    if (!props.loading) {
        formState.value = {
            id: props.todo.id,
            content: props.todo.content_en,
            bookmarked: props.todo.bookmarked,
            completed: props.todo.completed,
            date_deadline: props.todo.date_deadline
        };
    }
});
</script>