I am doing a little exercise with Vue, Pinia and Router and I am facing problems with retreiving data from a simple json-server I'll try to explain myself as shortly as possible:
Let's say my store config (simplified) is:
import { defineStore } from 'pinia'
import {fetchTasks} from '../utils/utils.js'
const SERVER_URL = "http://localhost:3001";
export const useTaskStore = defineStore('taskStore', {
state: () => ({
tasks: [],
singletask: []
}),
getters: {
},
},
actions: {
async getTasks() {
this.tasks = await fetchTasks(`${SERVER_URL}/tasks`)
},
async getSingleTask(id) {
this.singletask = await fetchTasks(`${SERVER_URL}/tasks/${id}`);
}
}
})
Function fetchTask in utils is simply:
const fetchTasks = async (url) => {
try {
const res = await fetch(url);
const data = await res.json();
return data;
} catch (e) {
console.error(e);
}
};
The response for e.g. http://localhost:3001/tasks is something like:
[
{
"id": 1,
"title": "First Task"
},
{
"id": 2,
"title": "Second Task"
},
{
"id": 3,
"title": "Third Task"
}
]
while the response for e.g. http://localhost:3001/tasks/1 is something like:
{
"id": 1,
"title": "First Task"
}
The component for the list of tasks is:
<script setup>
import TaskItemComponent from './TaskItem.vue'
import { useTaskStore } from '@/stores/taskStore'
import { storeToRefs } from 'pinia'
const taskStore = useTaskStore();
const {tasks} = storeToRefs(taskStore);
</script>
<template>
<div class="max-w-5xl mx-auto">
<template>
<TaskItemComponent v-for="t, index in tasks"
:key = "index"
:task = "t"
/>
</template>
</div>
</template>
and the TaskItemComponent component:
<script setup>
import { useTaskStore } from '@/stores/taskStore'
import { useRouter } from 'vue-router'
const props = defineProps(
{task: {type: Object} }
)
const taskStore = useTaskStore();
const router = useRouter();
const handleClick = () => {
router.push({name:'taskdetail',params:{'id':props.task.id}})
}
</script>
<template>
<div class="flex flex-row gap-2 bg-green-100 hover:bg-green-300 m-4 p-4 rounded-md cursor-pointer" @click="handleClick">
<h3>{{ task.title}}</h3>
<p class="text-xs">(id:{{ task.id }})</p>
</div>
</template>
And so far everything works fine.
The problem arises when I click on a "task item" to kick in the 'taskdetail' route - I want to fill the data fetched using the function getSingleTask
So far I wrote on the component for the task detail
<script setup>
import {onMounted,toRefs} from 'vue';
import { useTaskStore } from '@/stores/taskStore';
import { useRouter } from 'vue-router';
import { storeToRefs } from 'pinia'
const taskStore = useTaskStore();
const {singletask} = storeToRefs(taskStore)
const router = useRouter();
const props = defineProps(['id'])
const {id} = toRefs(props);
const getTask = (id) => {
taskStore.getSingleTask(id);
console.log(taskStore.singletask.id)
if (!taskStore.singletask.id) {
router.replace({name:'notfound'})
}
}
onMounted(()=>{
getTask(id.value);
})
</script>
<template>
<div class="max-w-5xl mx-auto p-8">
<template v-if="singletask.id">
<div class="mt-4 p-4 bg-red-300 text-xl font-bold rounded-t-xl">
ID:{{singletask.id}}
</div>
<div class="flex p-4 bg-green-200 h-96 items-center text-center align-middle rounded-b-xl">
{{singletask.title}}
</div>
</template>
</div>
</template>
where is the problem? The fist time I click on a task the app goes 404 because the value of 'taskStore.singletask.id' is undefined (checked from the console.log) I've understood that each time I click, the value of the id is the previous one clicked.
I don't understand what I am doing wrong
Thanks for the help, tell me if you need further info.
In taskdetail
route the getSingleTask
is called and before the completion of it you are trying to access the single task.
Try adding await
before taskStore.getSingleTask(id)
and make the getTask
to be an async function. (or do it with then .. catch
)
You could give loading message in singletask
state.