Skip to content

Commit b81b23c

Browse files
committed
feat: useCharacter (id) composable
1 parent e44cdd4 commit b81b23c

File tree

5 files changed

+83
-40
lines changed

5 files changed

+83
-40
lines changed

src/characters/composables/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
export * from './useCharacter';
12
export * from './useCharacters.old';
3+
export * from './useCharacters';
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { computed, ref } from "vue";
2+
3+
import type { Character } from "@/interfaces/character";
4+
import rickAndMortyApi from "@/api/rickAndMortyApi";
5+
import { useQuery } from "@tanstack/vue-query";
6+
import { isAxiosError } from "axios";
7+
8+
const characterSet = ref<{ [id: string]: Character }>({});
9+
const hasError = ref<boolean>(false);
10+
const errorMessage = ref<string | null>(null);
11+
12+
const getCharacter = async (id: string): Promise<Character> => {
13+
if (characterSet.value[id]) return characterSet.value[id];
14+
15+
const { data } = await rickAndMortyApi.get<Character>(`/character/${id}`);
16+
return data;
17+
};
18+
19+
const loadCharacter = (data: Character) => {
20+
hasError.value = false;
21+
errorMessage.value = null;
22+
characterSet.value[data.id] = data;
23+
};
24+
25+
const loadCharacterError = (error: unknown) => {
26+
hasError.value = true;
27+
if (isAxiosError(error)) {
28+
errorMessage.value = error.response?.data.error;
29+
}
30+
};
31+
32+
export const useCharacter = (id: string) => {
33+
const { isLoading } = useQuery<Character>(
34+
["character", id],
35+
() => getCharacter(id),
36+
{
37+
onSuccess: loadCharacter,
38+
onError: loadCharacterError,
39+
}
40+
);
41+
42+
return {
43+
// properties
44+
list: characterSet,
45+
hasError,
46+
errorMessage,
47+
isLoading,
48+
49+
// getters
50+
character: computed<Character | null>(() => characterSet.value[id]),
51+
};
52+
};
53+
54+
export default useCharacter;

src/characters/composables/useCharacters.ts

+2-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { isAxiosError } from 'axios';
77

88
// defined in the global state
99
const characters = ref<Character[]>([]);
10-
const isLoading = ref<boolean>(true);
1110
const hasError = ref<boolean>(false);
1211
const errorMessage = ref<string | null>(null);
1312

@@ -21,19 +20,17 @@ const loadCharacters = (data: Character[]) => {
2120
hasError.value = false;
2221
errorMessage.value = null;
2322
characters.value = data;
24-
isLoading.value = false;
2523
};
2624

2725
const loadError = (error: unknown) => {
2826
hasError.value = true;
2927
if (isAxiosError(error)) {
3028
errorMessage.value = error.response?.data.error;
3129
}
32-
isLoading.value = false;
3330
}
3431

35-
const useCharacters = () => {
36-
const {} = useQuery(["characters"], getCharacters, {
32+
export const useCharacters = () => {
33+
const { isLoading } = useQuery(["characters"], getCharacters, {
3734
onSuccess: loadCharacters,
3835
onError: loadError,
3936
});
@@ -51,5 +48,3 @@ const useCharacters = () => {
5148
// methods
5249
};
5350
};
54-
55-
export default useCharacters;

src/characters/pages/CharacterId.vue

+22-31
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,38 @@
11
<script setup lang="ts">
2-
import { useRoute } from "vue-router";
3-
import characterStore from "@/store/characters.store";
2+
import { watch, watchEffect } from "vue";
3+
import { useRoute, useRouter } from "vue-router";
4+
import { useCharacter } from "@/characters/composables";
45
5-
import rickAndMortyApi from "@/api/rickAndMortyApi";
6-
import type { Character } from "@/interfaces/character";
7-
import { useQuery } from "@tanstack/vue-query";
86
97
const route = useRoute();
8+
const router = useRouter();
109
1110
// in this case the reactivity will be lost
1211
const { id } = route.params as { id: string };
1312
14-
const getCharacterCacheFirst = async (
15-
characterId: string
16-
): Promise<Character> => {
17-
if (characterStore.checkId(characterId)) {
18-
return characterStore.ids.list[characterId];
19-
}
20-
21-
const { data } = await rickAndMortyApi.get<Character>(
22-
`/character/${characterId}`
23-
);
24-
return data;
25-
};
13+
const { character, isLoading, hasError, errorMessage } = useCharacter(id);
2614
27-
const { data: character } = useQuery<Character>(
28-
["character", id],
29-
() => getCharacterCacheFirst(id),
30-
{
31-
onSuccess: (data) => {
32-
characterStore.loadId(data);
33-
},
15+
watchEffect(() => {
16+
if (!isLoading.value && hasError.value) {
17+
router.replace("/characters");
3418
}
35-
);
19+
});
3620
</script>
3721

3822
<template>
3923
<div>
4024
<h1>Character #{{ id }}</h1>
41-
<h1 v-if="!character">Loading...</h1>
42-
<h1 v-else-if="characterStore.characters.hasError">
43-
{{ characterStore.characters.errorMessage }}
25+
<h1 v-if="isLoading">Loading...</h1>
26+
<h1 v-else-if="hasError">
27+
{{ errorMessage }}
4428
</h1>
45-
<div v-else class="card">
29+
<div v-else-if="character" class="card">
4630
<figure>
47-
<img :src="character.image" :alt="character.name" class="card-image" />
31+
<img
32+
:src="character.image"
33+
:alt="character.name"
34+
class="card-image"
35+
/>
4836
</figure>
4937
<div class="card-content">
5038
<h2>{{ character.name }}</h2>
@@ -83,7 +71,6 @@ figure {
8371
object-fit: contain;
8472
}
8573
86-
8774
.card-content {
8875
width: 100%;
8976
display: flex;
@@ -102,4 +89,8 @@ figure {
10289
display: flex;
10390
justify-content: center;
10491
}
92+
93+
h1 {
94+
margin: 2rem 0;
95+
}
10596
</style>

src/characters/pages/CharacterList.vue

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
<script setup lang="ts">
22
import CardList from "@/characters/components/CardList.vue";
3-
import useCharacters from "../composables/useCharacters";
3+
import { useCharacters } from "@/characters/composables";
44
55
const props = defineProps<{ title: string; visible: boolean }>();
6-
const { isLoading, hasError, errorMessage, characters, count } = useCharacters();
6+
const { isLoading, hasError, errorMessage, characters, count } =
7+
useCharacters();
78
</script>
89

910
<template>

0 commit comments

Comments
 (0)