Documentation Index Fetch the complete documentation index at: https://mintlify.com/EliasMari/Pokedex/llms.txt
Use this file to discover all available pages before exploring further.
Composables are reusable functions that leverage Vue’s Composition API to encapsulate and share stateful logic across components.
What are Composables?
Composables Composables are JavaScript functions that:
Use Vue’s Composition API (ref, computed, watch, etc.)
Encapsulate reusable reactive logic
Can be shared across multiple components
Follow the use* naming convention
useGetData Composable
The useGetData composable handles HTTP requests using Axios:
src/composables/useGetData.js
import axios from 'axios'
import { ref } from 'vue'
export const useGetData = () => {
const datos = ref ( null ); // Null because we don't know what we'll receive
const cargando = ref ( true );
const error = ref ( false );
const getData = async ( url ) => {
try {
// Wait to get results from the API
const resultado = await axios . get ( url );
datos . value = resultado . data ; // Axios always returns .data
}
catch ( err ) {
// Log error to console
console . log ( err );
error . value = true ;
}
finally {
cargando . value = false ;
}
};
return {
getData , // Return the function
datos , // Return the result obtained
error ,
cargando
}
};
Return Values
Type: (url: string) => Promise<void>Description: Async function that fetches data from the provided URLParameters:
url - API endpoint to fetch from
Side Effects:
Sets datos.value with response data
Sets error.value to true if request fails
Sets cargando.value to false when complete
Type: Ref<any | null>Description: Reactive reference containing the API response dataInitial Value: nullUsage: < div v-if = " datos " >
{{ datos.name }}
</ div >
Type: Ref<boolean>Description: Reactive reference indicating if the request failedInitial Value: falseUsage: < div v-if = " error " class = "error" >
Error loading data
</ div >
Type: Ref<boolean>Description: Reactive reference indicating if the request is in progressInitial Value: trueUsage: < div v-if = " cargando " class = "loading" >
Loading...
</ div >
Usage in Components
Basic Usage
< script setup >
import { useRoute } from 'vue-router'
import { useGetData } from '@/composables/useGetData'
const route = useRoute ()
const { getData , datos , error , cargando } = useGetData ()
// Fetch Pokemon data when component mounts
getData ( `https://pokeapi.co/api/v2/pokemon/ ${ route . params . nombre } ` )
</ script >
< template >
< div v-if = " cargando " class = "loading" >
< div class = "spinner" ></ div >
< p > Cargando información del Pokémon... </ p >
</ div >
< div v-else-if = " error " class = "error" >
< p > No se pudo cargar la información del Pokémon. </ p >
</ div >
< div v-else class = "pokemon-container" >
< h1 > {{ datos . name }} </ h1 >
<!-- Pokemon details -->
</ div >
</ template >
Multiple API Calls
You can create multiple instances of the composable:
src/views/PokemonsView.vue
< script setup >
import { ref , onMounted } from 'vue'
import { useGetData } from '@/composables/useGetData'
const { getData , datos , error , cargando } = useGetData ()
const offset = ref ( 0 )
const limit = ref ( 20 )
const fetchPokemons = () => {
getData ( `https://pokeapi.co/api/v2/pokemon?offset= ${ offset . value } &limit= ${ limit . value } ` )
}
onMounted (() => {
fetchPokemons ()
})
const next = () => {
offset . value += limit . value
fetchPokemons ()
}
const prev = () => {
if ( offset . value >= limit . value ) {
offset . value -= limit . value
fetchPokemons ()
}
}
</ script >
Reactive Fetching
Fetch data reactively when parameters change:
< script setup >
import { ref , watch } from 'vue'
import { useGetData } from '@/composables/useGetData'
const searchQuery = ref ( '' )
const { getData , datos , error , cargando } = useGetData ()
// Re-fetch when search query changes
watch ( searchQuery , ( newQuery ) => {
if ( newQuery ) {
getData ( `https://pokeapi.co/api/v2/pokemon/ ${ newQuery . toLowerCase () } ` )
}
})
</ script >
State Flow
The composable manages three states during the request lifecycle:
datos . value = null
cargando . value = true
error . value = false
// getData() called
cargando . value = true // Still true
error . value = false // Reset on new request
datos . value = { ... apiResponse }
cargando . value = false
error . value = false
datos . value = null // Unchanged
cargando . value = false
error . value = true
Template Patterns
Common template patterns when using useGetData:
Loading, Error, Success
< template >
< div v-if = " cargando " class = "loading" >
< div class = "spinner" ></ div >
< p > Cargando Pokémons... </ p >
</ div >
< div v-else-if = " error " class = "error" >
< p > Error al cargar los Pokémons. </ p >
</ div >
< div v-else class = "pokemon-grid" >
< div v-for = " poke in datos . results " : key = " poke . name " >
{{ poke . name }}
</ div >
</ div >
</ template >
With Empty State
< template >
< div v-if = " cargando " > Loading... </ div >
< div v-else-if = " error " > Error occurred </ div >
< div v-else-if = " ! datos || datos . length === 0 " > No results found </ div >
< div v-else >
<!-- Display data -->
</ div >
</ template >
Advantages of Composables
Benefits Reusability : Same logic can be used in multiple componentsOrganization : Separates business logic from component templatesTestability : Composables can be tested independentlyType Safety : Works well with TypeScriptFlexibility : Easy to customize and extend
Best Practices
Always Handle Loading States
Show feedback to users while data is loading: < div v-if = " cargando " class = "loading" >
<div class="spinner"> </ div >
< p > Loading... </ p >
< / div >
Always Handle Error States
Provide clear error messages when requests fail: < div v-if = " error " class = "error" >
<p>Failed to load data. Please try again.</p>
<button @click="retry">Retry</button>
</ div >
Check Data Before Rendering
Use optional chaining and nullish coalescing: < template >
< div > {{ datos ?. name ?? 'Unknown' }} </ div >
< div v-if = " datos ?. results " >
<!-- Render results -->
</ div >
</ template >
Reset State on New Requests
Consider resetting error state when making new requests: const getData = async ( url ) => {
error . value = false // Reset error
cargando . value = true
try {
const resultado = await axios . get ( url )
datos . value = resultado . data
} catch ( err ) {
error . value = true
} finally {
cargando . value = false
}
}
Extending useGetData
You can extend or modify the composable for specific needs:
Adding Retry Logic
export const useGetData = () => {
const datos = ref ( null )
const cargando = ref ( true )
const error = ref ( false )
const retryCount = ref ( 0 )
const maxRetries = 3
const getData = async ( url ) => {
try {
const resultado = await axios . get ( url )
datos . value = resultado . data
retryCount . value = 0 // Reset on success
} catch ( err ) {
console . log ( err )
error . value = true
if ( retryCount . value < maxRetries ) {
retryCount . value ++
setTimeout (() => getData ( url ), 1000 * retryCount . value )
}
} finally {
cargando . value = false
}
}
return { getData , datos , error , cargando , retryCount }
}
Adding Request Cancellation
import axios from 'axios'
import { ref } from 'vue'
export const useGetData = () => {
const datos = ref ( null )
const cargando = ref ( true )
const error = ref ( false )
let cancelToken = null
const getData = async ( url ) => {
// Cancel previous request if exists
if ( cancelToken ) {
cancelToken . cancel ( 'New request initiated' )
}
cancelToken = axios . CancelToken . source ()
try {
const resultado = await axios . get ( url , {
cancelToken: cancelToken . token
})
datos . value = resultado . data
} catch ( err ) {
if ( ! axios . isCancel ( err )) {
error . value = true
}
} finally {
cargando . value = false
}
}
return { getData , datos , error , cargando }
}
Next Steps
Project Structure Learn about the codebase organization
State Management Understand Pinia stores for global state