I have a component which needs some external data (API) to fill some content in the template through asynchronous functions. I reduced it the more I could and renamed some variables for better understanding.
The component:
<template>
<div>
<h3>Scheduled Events</h3>
<div v-if="error">{{ error }}</div>
<div v-if="nextEvent">
<h1>{{ nextEvent.event.name }}</h1>
<p>test</p>
</div>
</div>
</template>
<script>
export default {
components: { EventSnippet },
setup() {
const { error, getNextEvents, getNextEventDetails } = useSchedule(2025);
const { nextEvent } = getNextEventDetails();
return { error, nextEvent };
},
};
</script>
and the composable:
import { ref } from "vue";
import callApi from "./API/callAPI";
const useSchedule = (year) => {
const error = ref(null);
const getSchedule = async () => {
const query = {
request: "getEventId",
params: { year: year, league: "xyz" },
};
const res = await callApi(query);
return res;
};
const getNextEvents = async () => {
const nextEvents = ref(null);
const yearSchedule = await getSchedule();
const onlyFutureEvents = yearSchedule.result.filter((event) => {
return new Date(event.startDate).getTime() > new Date().getTime();
});
const arrayNextSorted = onlyFutureEvents.sort(
(event, today = new Date()) => {
const eventDate = new Date(event.date);
console.log(eventDate - today);
return eventDate - today;
}
);
nextEvents.value = arrayNextSorted;
return nextEvents;
};
const getNextEventDetails = async () => {
const nextEvent = ref(null);
try {
const nextEvents = await getNextEvents();
const nextEventid = nextEvents.value[0].eventId;
const query = {
request: "getEventDetails",
params: { league: "xyz", eventId: nextEventid },
};
const res = await callApi(query);
nextEvent.value = res;
} catch (err) {
error.value = err.message;
}
return { nextEvent };
};
return { error, getNextEvents, getNextEventDetails };
};
export default useSchedule;
I'll quickly explain why I tried with this composable solution:
getSchedule returns all eventsgetNextEvents returns all events that haven't happened yet (I want to re-use it elsewhere) in the appgetNextEventDetails returns only the next event to come and its details (it needs another API call).I don't pretend this is an OK code, probably ill-written, it's just the best I could do based on my little experience.
Now, whenever I console log the values through the composable, they are all correct. I feel like I lost myself with all the async/await maybe and for sure with ref() reactivity, too.
Try to separate the state from the inner functions and hoist them in the main composable function; then just destruct the composable with functions and state :
import { ref } from "vue";
import callApi from "./API/callAPI";
const useSchedule = (year) => {
const error = ref(null);
const nextEvents = ref([]);
const nextEvent = ref(null);
const getSchedule = async () => {
const query = {
request: "getEventId",
params: { year: year, league: "xyz" },
};
const res = await callApi(query);
return res;
};
const getNextEvents = async () => {
const yearSchedule = await getSchedule();
const onlyFutureEvents = yearSchedule.result.filter((event) => {
return new Date(event.startDate).getTime() > new Date().getTime();
});
const arrayNextSorted = onlyFutureEvents.sort(
(event, today = new Date()) => {
const eventDate = new Date(event.date);
console.log(eventDate - today);
return eventDate - today;
}
);
nextEvents.value = arrayNextSorted;
};
const getNextEventDetails = async () => {
try {
const nextEvents = await getNextEvents();
const nextEventid = nextEvents.value[0].eventId;
const query = {
request: "getEventDetails",
params: { league: "xyz", eventId: nextEventid },
};
const res = await callApi(query);
nextEvent.value = res;
} catch (err) {
error.value = err.message;
}
};
return { error, getNextEvents, getNextEventDetails, nextEvent , nextEvents };
};
export default useSchedule;
then use the composable as follows :
<script>
export default {
components: { EventSnippet },
setup() {
const { error, getNextEvents, getNextEventDetails, nextEvent } = useSchedule(2025);
// call the function that mutates the state
getNextCardDetails();
return { error, nextEvent };
},
};
</script>