¿useFetch o usePaginatedFetch?
Cargar datos de la api tiene exactamente dos formas, y ninguna pantalla debería escribir la tercera.
| Hook | Para | Devuelve |
|---|---|---|
useFetch |
Un recurso suelto: el perfil, los ajustes, una mascota | { data, loading, error, setData, reload } |
usePaginatedFetch |
Listas que crecen con scroll | { items, total, loading, loadingMore, error, hasMore, loadMore } |
Si el endpoint devuelve una lista paginada,
usePaginatedFetch. Para todo lo demás,useFetch.
Encima de ellos, un hook por recurso que solo aporta la llamada y el mensaje de error. Así se leen en una pantalla:
const { data: profile, loading, setData } = useProfile();
Y así se escribe uno nuevo — suelen ser tres líneas:
export function usePetReports(petId: string) {
return usePaginatedFetch((page, limit) => listReports(petId, page, limit), petId, 10);
}
El parámetro key
Es lo que hace que el hook se recargue cuando cambia el recurso, no cuando cambia cualquier cosa:
- Pásalo si la carga depende de un id (
petId). Al cambiar, vuelve a pedir y limpia los datos del anterior — sin eso, la pantalla de una mascota enseñaría un instante los datos de la otra. - Omítelo si el endpoint siempre devuelve lo mismo para el usuario actual, como
/users/me.
setData es para actualizaciones optimistas
useFetch expone el setData a propósito: escribe el valor nuevo al momento y revierte si la
llamada falla. Es lo que hace que un interruptor de ajustes se sienta instantáneo.
Por qué el estado no se toca dentro del efecto
Es la parte del código con más razonamiento detrás, y la que alguien intentará “arreglar”:
const [loading, setLoading] = useState(true); // ya arranca cargando
// ...
const [lastKey, setLastKey] = useState(key);
if (key !== lastKey) { // durante el render, no en un efecto
setLastKey(key);
setData(null);
setLoading(true);
}
Dos decisiones ahí:
loadingempieza entruey solo se reinicia desdereload(). No hace falta ponerlo dentro del efecto porque el estado inicial ya es el estado de carga.- El reinicio al cambiar
keyse hace durante el render, que es la recomendación de React para “ajustar estado cuando cambia una prop”.
El motivo de fondo: escribir estado en el cuerpo de un efecto provoca un render en cascada, y la
regla react-hooks/set-state-in-effect lo marca. Hacerlo así mantiene el hook limpio y el lint
en verde.
El guard del token
Los dos hooks empiezan igual:
if (!accessToken) return;
No es defensivo por si acaso: apiFetch lee el token de una variable de módulo que se rellena como
efecto secundario del login, así que un hook no puede fiarse del orden de montaje. Sin este
guard, una pantalla que monta antes de que la sesión se restaure lanzaría la petición sin
Authorization. Ver hablar con la api.
La dependencia que se excluye a propósito
Los dos hooks llevan un eslint-disable sobre exhaustive-deps para omitir la función de carga.
Está justificado y comentado: esa función se recrea en cada render pero siempre encierra key, que
es la dependencia real. Incluirla provocaría una petición por render.
Es la única excepción aceptada a exhaustive-deps en el repo. Si te hace falta otra, casi
siempre significa que la función debería estar envuelta en useCallback en su origen.