Data Source
Data Source es un envoltorio sencillo para la obtención de datos. Es una especie de "puerto" en la arquitectura limpia. Permite crear envoltorios para la obtención de datos en función de tus casos de uso. Data Source utiliza react-query internamente.
Instalación
npm install @gravity-ui/data-source @tanstack/react-query
@tanstack/react-query
es una dependencia peer.
Inicio Rápido
1. Configurar DataManager
Primero, crea y proporciona un DataManager
en tu aplicación:
import React from 'react';
import {ClientDataManager, DataManagerContext} from '@gravity-ui/data-source';
const dataManager = new ClientDataManager({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 minutos
retry: 3,
},
// ... otras opciones de react-query
},
});
function App() {
return (
<DataManagerContext.Provider value={dataManager}>
<YourApplication />
</DataManagerContext.Provider>
);
}
2. Definir Tipos de Error y Envoltorios
Define un tipo de error y crea tus constructores para las fuentes de datos basándote en los constructores predeterminados:
import {makePlainQueryDataSource as makePlainQueryDataSourceBase} from '@gravity-ui/data-source';
export interface ApiError {
code: number;
title: string;
description?: string;
}
export const makePlainQueryDataSource = <TParams, TRequest, TResponse, TData, TError = ApiError>(
config: Omit<PlainQueryDataSource<TParams, TRequest, TResponse, TData, TError>, 'type'>,
): PlainQueryDataSource<TParams, TRequest, TResponse, TData, TError> => {
return makePlainQueryDataSourceBase(config);
};
3. Crear Componente DataLoader Personalizado
Escribe un componente DataLoader
basado en el predeterminado para definir tu visualización del estado de carga y los errores:
import {
DataLoader as DataLoaderBase,
DataLoaderProps as DataLoaderPropsBase,
ErrorViewProps,
} from '@gravity-ui/data-source';
export interface DataLoaderProps
extends Omit<DataLoaderPropsBase<ApiError>, 'LoadingView' | 'ErrorView'> {
LoadingView?: ComponentType;
ErrorView?: ComponentType<ErrorViewProps<ApiError>>;
}
export const DataLoader: React.FC<DataLoaderProps> = ({
LoadingView = YourLoader, // Puedes usar tu propio componente de carga
ErrorView = YourError, // Puedes usar tu propio componente de error
...restProps
}) => {
return <DataLoaderBase LoadingView={LoadingView} ErrorView={ErrorView} {...restProps} />;
};
4. Definir Tu Primera Fuente de Datos
import {skipContext} from '@gravity-ui/data-source';
// Tu función de API
import {fetchUser} from './api';
export const userDataSource = makePlainQueryDataSource({
// Las claves deben ser únicas. Quizás deberías crear un helper para generar nombres de fuentes de datos
name: 'user',
// skipContext es un helper para omitir los 2 primeros parámetros de la función (context y fetchContext)
fetch: skipContext(fetchUser),
// Opcional: generar etiquetas para la invalidación avanzada de caché
tags: (params) => [`user:${params.userId}`, 'users'],
});
5. Usar en Componentes
import {useQueryData} from '@gravity-ui/data-source';
export const UserProfile: React.FC<{userId: number}> = ({userId}) => {
const {data, status, error, refetch} = useQueryData(userDataSource, {userId});
return (
<DataLoader status={status} error={error} errorAction={refetch}>
{data && <UserCard user={data} />}
</DataLoader>
);
};
Conceptos Clave
Tipos de Fuentes de Datos
La biblioteca proporciona dos tipos principales de fuentes de datos:
Fuente de Datos de Consulta Simple (Plain Query Data Source)
Para patrones simples de solicitud/respuesta:
const userDataSource = makePlainQueryDataSource({
name: 'user',
fetch: skipContext(async (params: {userId: number}) => {
const response = await fetch(`/api/users/${params.userId}`);
return response.json();
}),
});
Fuente de Datos de Consulta Infinita (Infinite Query Data Source)
Para paginación y desplazamiento infinito:
const postsDataSource = makeInfiniteQueryDataSource({
name: 'posts',
fetch: skipContext(async (params: {page: number; limit: number}) => {
const response = await fetch(`/api/posts?page=${params.page}&limit=${params.limit}`);
return response.json();
}),
next: (lastPage, allPages) => {
if (lastPage.hasNext) {
return {page: allPages.length + 1, limit: 20};
}
return undefined;
},
});
Gestión de Estados
La biblioteca normaliza los estados de las consultas en tres estados simples:
loading
- Carga de datos real. Lo mismo queisLoading
en React Query.success
- Datos disponibles (puede omitirse usandoidle
).error
- Falló la obtención de datos.
Concepto de Idle
La biblioteca proporciona un símbolo especial idle
para omitir la ejecución de la consulta:
import {idle} from '@gravity-ui/data-source';
const UserProfile: React.FC<{userId?: number}> = ({userId}) => {
// La consulta no se ejecutará si userId no está definido
const {data, status} = useQueryData(userDataSource, userId ? {userId} : idle);
return (
<DataLoader status={status} error={null}>
{data && <UserCard user={data} />}
</DataLoader>
);
};
Cuando los parámetros son iguales a idle
:
- La consulta no se ejecuta.
- El estado permanece
success
. - Los datos permanecen
undefined
. - El componente puede renderizarse de forma segura sin cargar.
Beneficios de idle
:
- Seguridad de Tipos - TypeScript infiere correctamente los tipos para parámetros condicionales.
- Rendimiento - Evita solicitudes de servidor innecesarias.
- Simplicidad Lógica - No es necesario gestionar un estado
enabled
adicional. - Consistencia - Enfoque unificado para todas las consultas condicionales.
Esto es especialmente útil para consultas condicionales cuando deseas cargar datos solo bajo ciertas condiciones, manteniendo al mismo tiempo la seguridad de tipos.
Referencia de la API
Creación de Fuentes de Datos
makePlainQueryDataSource(config)
Crea una fuente de datos de consulta simple para patrones de solicitud/respuesta.
const dataSource = makePlainQueryDataSource({
name: 'unique-name',
fetch: skipContext(fetchFunction),
transformParams: (params) => transformedRequest,
transformResponse: (response) => transformedData,
tags: (params) => ['tag1', 'tag2'],
options: {
staleTime: 60000,
retry: 3,
// ... otras opciones de react-query
},
});
Parámetros:
name
- Identificador único para la fuente de datos.fetch
- Función que realiza la obtención real de datos.transformParams
(opcional) - Transforma los parámetros de entrada antes de la solicitud.transformResponse
(opcional) - Transforma los datos de respuesta.tags
(opcional) - Genera etiquetas de caché para invalidación.options
(opcional) - Opciones de React Query.
makeInfiniteQueryDataSource(config)
Crea una fuente de datos de consulta infinita para patrones de paginación y desplazamiento infinito.
const infiniteDataSource = makeInfiniteQueryDataSource({
name: 'infinite-data',
fetch: skipContext(fetchFunction),
next: (lastPage, allPages) => nextPageParam || undefined,
prev: (firstPage, allPages) => prevPageParam || undefined,
// ... otras opciones iguales que las de consulta simple
});
Parámetros Adicionales:
next
- Función para determinar los parámetros de la siguiente página.prev
(opcional) - Función para determinar los parámetros de la página anterior.
Hooks de React
useQueryData(dataSource, params, options?)
Hook principal para obtener datos con una fuente de datos.
const {data, status, error, refetch, ...rest} = useQueryData(
userDataSource,
{userId: 123},
{
enabled: true,
refetchInterval: 30000,
},
);
Devuelve:
data
- Los datos obtenidos.status
- Estado actual ('loading' | 'success' | 'error').error
- Objeto de error si la solicitud falló.refetch
- Función para volver a obtener los datos manualmente.- Otras propiedades de React Query.
useQueryResponses(responses)
Combina múltiples respuestas de consulta en un único estado.
const user = useQueryData(userDataSource, {userId});
const posts = useQueryData(postsDataSource, {userId});
const {status, error, refetch, refetchErrored} = useQueryResponses([user, posts]);
Devuelve:
status
- Estado combinado de todas las consultas.error
- Primer error encontrado.refetch
- Función para volver a obtener todas las consultas.refetchErrored
- Función para volver a obtener solo las consultas fallidas.
useRefetchAll(states)
Crea una función de devolución de llamada para volver a obtener múltiples consultas.
const refetchAll = useRefetchAll([user, posts, comments]);
// refetchAll() activará la reobtención de todos los datos de las consultas
useRefetchErrored(states)
Crea una función de devolución de llamada para volver a obtener solo las consultas fallidas.
const refetchErrored = useRefetchErrored([user, posts, comments]);
// refetchErrored() solo volverá a obtener las consultas con errores
useDataManager()
Devuelve el DataManager
del contexto.
const dataManager = useDataManager();
await dataManager.invalidateTag('users');
useQueryContext()
Devuelve el contexto de la consulta (para construir hooks de datos personalizados basados en react-query
).
Componentes de React
<DataLoader />
Componente para manejar estados de carga y errores.
<DataLoader
status={status}
error={error}
errorAction={refetch}
LoadingView={SpinnerComponent}
ErrorView={ErrorComponent}
loadingViewProps={{size: 'large'}}
errorViewProps={{showDetails: true}}
>
{data && <YourContent data={data} />}
</DataLoader>
Props:
status
- Estado de carga actual.error
- Objeto de error.errorAction
- Función o configuración de acción para reintentar el error.LoadingView
- Componente a mostrar durante la carga.ErrorView
- Componente a mostrar en caso de error.loadingViewProps
- Props pasados aLoadingView
.errorViewProps
- Props pasados aErrorView
.
<DataInfiniteLoader />
Componente especializado para consultas infinitas.
<DataInfiniteLoader
status={status}
error={error}
hasNextPage={hasNextPage}
fetchNextPage={fetchNextPage}
isFetchingNextPage={isFetchingNextPage}
LoadingView={SpinnerComponent}
ErrorView={ErrorComponent}
MoreView={LoadMoreButton}
>
{data.map((item) => (
<Item key={item.id} data={item} />
))}
</DataInfiniteLoader>
Props Adicionales:
hasNextPage
- Si hay más páginas disponibles.fetchNextPage
- Función para obtener la siguiente página.isFetchingNextPage
- Si se está obteniendo la siguiente página.MoreView
- Componente para el botón "cargar más".
withDataManager(Component)
HOC que inyecta DataManager
como una prop.
const MyComponent = withDataManager<Props>(({dataManager, ...props}) => {
// El componente tiene acceso a dataManager
return <div>...</div>;
});
Gestión de Datos
ClientDataManager
Clase principal para la gestión de datos.
const dataManager = new ClientDataManager({
defaultOptions: {
queries: {
staleTime: 300000, // 5 minutos
retry: 3,
refetchOnWindowFocus: false,
},
},
});
Métodos:
invalidateTag(tag, options?)
Invalida todas las consultas con una etiqueta específica.
await dataManager.invalidateTag('users');
await dataManager.invalidateTag('posts', {
repeat: {count: 3, interval: 1000}, // Reintentar invalidación
});
invalidateTags(tags, options?)
Invalida las consultas que tienen todas las etiquetas especificadas.
await dataManager.invalidateTags(['user', 'profile']);
invalidateSource(dataSource, options?)
Invalida todas las consultas para una fuente de datos.
await dataManager.invalidateSource(userDataSource);
invalidateParams(dataSource, params, options?)
Invalida una consulta específica con parámetros exactos.
await dataManager.invalidateParams(userDataSource, {userId: 123});
resetSource(dataSource)
Reinicia (borra) todos los datos cacheados para una fuente de datos.
await dataManager.resetSource(userDataSource);
resetParams(dataSource, params)
Reinicia los datos cacheados para parámetros específicos.
await dataManager.resetParams(userDataSource, {userId: 123});
invalidateSourceTags(dataSource, params, options?)
Invalida consultas basándose en las etiquetas generadas por una fuente de datos.
await dataManager.invalidateSourceTags(userDataSource, {userId: 123});
Utilidades
skipContext(fetchFunction)
Utilidad para adaptar funciones fetch
existentes a la interfaz de fuente de datos.
// Función existente
async function fetchUser(params: {userId: number}) {
// ...
}
// Adaptada para la fuente de datos
const dataSource = makePlainQueryDataSource({
name: 'user',
fetch: skipContext(fetchUser), // Omite el contexto y los parámetros de fetchContext
});
withCatch(fetchFunction, errorHandler)
Añade manejo de errores estandarizado a las funciones fetch.
const safeFetch = withCatch(fetchUser, (error) => ({error: true, message: error.message}));
withCancellation(fetchFunction)
Añade soporte de cancelación a las funciones fetch.
const cancellableFetch = withCancellation(fetchFunction);
// Maneja automáticamente AbortSignal de React Query
getProgressiveRefetch(options)
Crea una función de intervalo de refetch progresivo.
const progressiveRefetch = getProgressiveRefetch({
minInterval: 1000, // Empieza con 1 segundo
maxInterval: 30000, // Máximo 30 segundos
multiplier: 2, // Duplica cada vez
});
const dataSource = makePlainQueryDataSource({
name: 'data',
fetch: skipContext(fetchData),
options: {
refetchInterval: progressiveRefetch,
},
});
normalizeStatus(status, fetchStatus)
Convierte los estados de React Query a estados de DataLoader.
const status = normalizeStatus('pending', 'fetching'); // 'loading'
Utilidades de Estado y Error
// Obtiene el estado combinado de múltiples estados
const status = getStatus([user, posts, comments]);
// Obtiene el primer error de múltiples estados
const error = getError([user, posts, comments]);
// Combina múltiples estados
const combinedStatus = mergeStatuses(['loading', 'success', 'error']); // 'error'
// Comprueba si una clave de consulta tiene una etiqueta
const hasUserTag = hasTag(queryKey, 'users');
Utilidades de Composición de Claves
// Compone la clave de caché para una fuente de datos
const key = composeKey(userDataSource, {userId: 123});
// Compone la clave completa incluyendo etiquetas
const fullKey = composeFullKey(userDataSource, {userId: 123});
Constantes
import {idle} from '@gravity-ui/data-source';
// Símbolo especial para omitir la ejecución de la consulta
const params = shouldFetch ? {userId: 123} : idle;
// Alternativa segura a nivel de tipo para enabled: false
// En lugar de:
const {data} = useQueryData(userDataSource, {userId: userId || ''}, {enabled: Boolean(userId)});
// Usa:
const {data} = useQueryData(userDataSource, userId ? {userId} : idle);
// TypeScript infiere correctamente los tipos para ambas ramas
Composición de Opciones de Consulta
// Compone las opciones de React Query para consultas simples
const plainOptions = composePlainQueryOptions(context, dataSource, params, options);
// Compone las opciones de React Query para consultas infinitas
const infiniteOptions = composeInfiniteQueryOptions(context, dataSource, params, options);
Nota: Estas funciones se utilizan principalmente internamente al crear implementaciones personalizadas de fuentes de datos.
Patrones Avanzados
idle
Consultas Condicionales con Usa idle
para crear consultas condicionales:
import {idle} from '@gravity-ui/data-source';
const ConditionalDataComponent: React.FC<{
userId?: number;
shouldLoadPosts: boolean;
}> = ({userId, shouldLoadPosts}) => {
// Carga el usuario solo si userId está definido
const user = useQueryData(
userDataSource,
userId ? {userId} : idle
);
// Carga las publicaciones solo si el usuario está cargado y la bandera está habilitada
const posts = useQueryData(
userPostsDataSource,
user.data && shouldLoadPosts ? {userId: user.data.id} : idle
);
const combined = useQueryResponses([user, posts]);
return (
<DataLoader status={combined.status} error={combined.error}>
<div>
{user.data && <UserInfo user={user.data} />}
{posts.data && <UserPosts posts={posts.data} />}
</div>
</DataLoader>
);
};
Transformación de Datos
Transforma los parámetros de solicitud y los datos de respuesta:
const apiDataSource = makePlainQueryDataSource({
name: 'api-data',
transformParams: (params: {id: number}) => ({
userId: params.id,
apiVersion: 'v2',
format: 'json',
}),
transformResponse: (response: ApiResponse) => ({
user: response.data.user,
metadata: response.meta,
}),
fetch: skipContext(apiFetch),
});
Invalidez de Caché Basada en Etiquetas
Usa etiquetas para una gestión de caché sofisticada:
const userDataSource = makePlainQueryDataSource({
name: 'user',
tags: (params) => [`user:${params.userId}`, 'users', 'profiles'],
fetch: skipContext(fetchUser),
});
const userPostsDataSource = makePlainQueryDataSource({
name: 'user-posts',
tags: (params) => [`user:${params.userId}`, 'posts'],
fetch: skipContext(fetchUserPosts),
});
// Invalida todos los datos de un usuario específico
await dataManager.invalidateTag('user:123');
// Invalida todos los datos relacionados con el usuario
await dataManager.invalidateTag('users');
Manejo de Errores con Tipos
Crea un manejo de errores seguro a nivel de tipo:
interface ApiError {
code: number;
message: string;
details?: Record<string, unknown>;
}
const ErrorView: React.FC<ErrorViewProps<ApiError>> = ({error, action}) => (
<div className="error">
<h3>Error {error?.code}</h3>
<p>{error?.message}</p>
{action && (
<button onClick={action.handler}>
{action.children || 'Reintentar'}
</button>
)}
</div>
);
Consultas Infinitas con Paginación Compleja
Maneja escenarios de paginación complejos:
interface PaginationParams {
cursor?: string;
limit?: number;
filters?: Record<string, unknown>;
}
interface PaginatedResponse<T> {
data: T[];
nextCursor?: string;
hasMore: boolean;
}
const infiniteDataSource = makeInfiniteQueryDataSource({
name: 'paginated-data',
fetch: skipContext(async (params: PaginationParams) => {
const response = await fetch(`/api/data?${new URLSearchParams(params)}`);
return response.json() as PaginatedResponse<DataItem>;
}),
next: (lastPage) => {
if (lastPage.hasMore && lastPage.nextCursor) {
return {cursor: lastPage.nextCursor, limit: 20};
}
return undefined;
},
});
Combinación de Múltiples Fuentes de Datos
Combina datos de múltiples fuentes:
const UserProfile: React.FC<{userId: number}> = ({userId}) => {
const user = useQueryData(userDataSource, {userId});
const posts = useQueryData(userPostsDataSource, {userId});
const followers = useQueryData(userFollowersDataSource, {userId});
const combined = useQueryResponses([user, posts, followers]);
<p>
<a href="https://github.com/gravity-ui/data-sources/blob/main/README.md">English</a> |
<a href="https://github.com/gravity-ui/data-sources/blob/main/README.es.md">Español</a>
</p>
@gravity-ui/data-sources
Este paquete proporciona un conjunto de utilidades para gestionar el estado de las fuentes de datos en aplicaciones React. Permite manejar fácilmente los estados de carga, error y éxito, así como la refetching de datos.
Instalación
npm install @gravity-ui/data-sources
# o
yarn add @gravity-ui/data-sources
Uso
makeQueryDataSource
La forma más común de crear una fuente de datos es usando makeQueryDataSource
.
import { makeQueryDataSource, skipContext } from '@gravity-ui/data-sources';
interface User {
id: number;
name: string;
}
const fetchUser = skipContext(async (params: { userId: number }): Promise<User> => {
// Simula una llamada a la API
await new Promise(resolve => setTimeout(resolve, 1000));
if (params.userId === 404) {
throw new Error('User not found');
}
return { id: params.userId, name: `User ${params.userId}` };
});
const userDataSource = makeQueryDataSource({
name: 'user',
fetch: fetchUser,
});
// En tu componente React:
import { useQueryData } from '@gravity-ui/data-sources';
const UserProfile = ({ userId }: { userId: number }) => {
const { data: user, isLoading, error, refetch } = useQueryData(userDataSource, { userId });
if (isLoading) {
return <div>Cargando perfil...</div>;
}
if (error) {
return (
<div>
Error al cargar el perfil: {error.message}
<button onClick={refetch}>Reintentar</button>
</div>
);
}
return (
<div>
<h1>{user.name}</h1>
<p>ID: {user.id}</p>
</div>
);
};
makeMutationDataSource
Para operaciones que modifican datos (mutaciones).
import { makeMutationDataSource, skipContext } from '@gravity-ui/data-sources';
interface Post {
id: number;
title: string;
}
const createPost = skipContext(async (data: { title: string }): Promise<Post> => {
await new Promise(resolve => setTimeout(resolve, 1000));
return { id: Date.now(), title: data.title };
});
const createPostDataSource = makeMutationDataSource({
name: 'create-post',
fetch: createPost,
});
// En tu componente React:
import { useMutationData } from '@gravity-ui/data-sources';
const NewPostForm = () => {
const { mutate, isLoading, error } = useMutationData(createPostDataSource);
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
const title = formData.get('title') as string;
await mutate({ title });
};
return (
<form onSubmit={handleSubmit}>
<input type="text" name="title" placeholder="Título de la publicación" />
<button type="submit" disabled={isLoading}>
{isLoading ? 'Creando...' : 'Crear Publicación'}
</button>
{error && <div>Error: {error.message}</div>}
</form>
);
};
makePlainQueryDataSource
Una versión más simple de makeQueryDataSource
que no maneja automáticamente los parámetros.
import { makePlainQueryDataSource, skipContext } from '@gravity-ui/data-sources';
interface Product {
id: string;
name: string;
}
const fetchProduct = skipContext(async (): Promise<Product[]> => {
await new Promise(resolve => setTimeout(resolve, 1000));
return [{ id: '1', name: 'Producto A' }, { id: '2', name: 'Producto B' }];
});
const productsDataSource = makePlainQueryDataSource({
name: 'products',
fetch: fetchProduct,
});
// En tu componente React:
import { useQueryData } from '@gravity-ui/data-sources';
const ProductList = () => {
const { data: products, isLoading, error } = useQueryData(productsDataSource);
if (isLoading) return <div>Cargando productos...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{products.map(product => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
};
DataLoader
Un componente de utilidad para envolver tus componentes y manejar estados de carga y error de forma declarativa.
import { DataLoader } from '@gravity-ui/data-sources';
import { useQueryData } from '@gravity-ui/data-sources';
import { makeQueryDataSource, skipContext } from '@gravity-ui/data-sources';
// Supongamos que tienes estos componentes definidos:
// const ProfileSkeleton = () => <div>Esqueleto de perfil...</div>;
// const ProfileError = ({ error, retry }: { error: Error; retry: () => void }) => (
// <div>
// Error: {error.message} <button onClick={retry}>Reintentar</button>
// </div>
// );
// const UserInfo = ({ user }: { user: User }) => <div>{user.name}</div>;
// const UserPosts = ({ posts }: { posts: Post[] }) => <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
// const UserFollowers = ({ followers }: { followers: Follower[] }) => <div>{followers.length} seguidores</div>;
// Definiciones de fuentes de datos (ejemplo)
interface User { id: number; name: string; }
interface Post { id: number; title: string; }
interface Follower { id: number; name: string; }
const fetchUser = skipContext(async (params: { userId: number }): Promise<User> => { /* ... */ });
const fetchPosts = skipContext(async (params: { userId: number }): Promise<Post[]> => { /* ... */ });
const fetchFollowers = skipContext(async (params: { userId: number }): Promise<Follower[]> => { /* ... */ });
const userDataSource = makeQueryDataSource({ name: 'user', fetch: fetchUser });
const postsDataSource = makeQueryDataSource({ name: 'posts', fetch: fetchPosts });
const followersDataSource = makeQueryDataSource({ name: 'followers', fetch: fetchFollowers });
const UserProfileWithDataLoader = ({ userId }: { userId: number }) => {
const user = useQueryData(userDataSource, { userId });
const posts = useQueryData(postsDataSource, { userId });
const followers = useQueryData(followersDataSource, { userId });
const combined = useQueryData([user, posts, followers]); // Combina múltiples fuentes de datos
return (
<DataLoader
status={combined.status}
error={combined.error}
errorAction={combined.refetchErrored} // Solo reintenta las solicitudes fallidas
LoadingView={ProfileSkeleton}
ErrorView={ProfileError}
>
{user.data && posts.data && followers.data && (
<div>
<UserInfo user={user.data} />
<UserPosts posts={posts.data} />
<UserFollowers followers={followers.data} />
</div>
)}
</DataLoader>
);
};
Soporte de TypeScript
La biblioteca está construida con un enfoque "TypeScript-first" y proporciona inferencia de tipos completa:
// Los tipos se infieren automáticamente
const userDataSource = makePlainQueryDataSource({
name: 'user',
fetch: skipContext(async (params: {userId: number}): Promise<User> => {
// El tipo de retorno se infiere como User
}),
});
// El tipo de retorno del hook se tipifica automáticamente
const {data} = useQueryData(userDataSource, {userId: 123});
// data tiene el tipo User | undefined
Tipos de Error Personalizados
Define y utiliza tipos de error personalizados:
interface ValidationError {
field: string;
message: string;
}
interface ApiError {
type: 'network' | 'validation' | 'server';
message: string;
validation?: ValidationError[];
}
const typedDataSource = makePlainQueryDataSource<
{id: number}, // Tipo de parámetros
{id: number}, // Tipo de solicitud
ApiResponse, // Tipo de respuesta
User, // Tipo de datos
ApiError // Tipo de error
>({
name: 'typed-user',
fetch: skipContext(fetchUser),
});
Contribución
Por favor, lee CONTRIBUTING.md para obtener detalles sobre nuestro código de conducta y el proceso para enviar pull requests.
Licencia
Licencia MIT. Consulta el archivo LICENSE para obtener más detalles.