React Query - por que isso importa?

1 de Setembro, 20252 min de leitura
Featured image for article: React Query - por que isso importa? - Escape do inferno de useEffect e desbloqueie recursos poderosos como caching, retries e suspense automaticamente. React ...
#react#javascript#webdev

Evita o inferno de useEffect e gerencia: estado de requisições, cache, refetch, retry, "suspending" e tratamento de erros; tudo pronto para uso.

Ajuda com o gerenciamento de Estado Assíncrono.

Inferno de useEffect

Você provavelmente não precisa de useEffect, especialmente para lidar com requisições.

Diferença de código

ruim

import { useState, useEffect } from 'react'; const UniverseList = () => { const [isLoading, setLoading] = useState(true); const [error, setError] = useState(null); const [universes, setUniverses] = useState([]); useEffect(() => { const controller = new AbortController(); const loadUniverses = async () => { try { setLoading(true); setError(null); const response = await fetch('/api/universes', { signal: controller.signal }); if (!response.ok) { throw new Error(`Error: ${response.status} - ${await response.text()}`); } const jsonResponse = await response.json(); setUniverses(jsonResponse.data || []); } catch (err) { if (err.name !== 'AbortError') { setError(err.message || 'Failed to load universes'); } } finally { setLoading(false); } }; loadUniverses(); return () => { controller.abort(); }; }, []); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error: {error}</div>; return ( <div> {universes.map(universe => ( <div key={universe.id}>{universe.name}</div> ))} </div> ); }; export default UniverseList;

bom

import { Suspense } from 'react'; import { useSuspenseQuery } from '@tanstack/react-query'; import { ErrorBoundary } from 'react-error-boundary'; const FIVE_MINUTES = 5 * 60 * 1_000; const TEN_MINUTES = 10 * 60 * 1_000; const fetchUniverses = async () => { const response = await fetch('/api/universes'); if (!response.ok) { throw new Error(`Error: ${response.status} - ${await response.text()}`); } const jsonResponse = await response.json(); return jsonResponse.data || []; }; const UniverseListContent = () => { const { data: universes } = useSuspenseQuery({ queryKey: ['universes'], queryFn: fetchUniverses, staleTime: FIVE_MINUTES, gcTime: TEN_MINUTES, retry: 3, retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000), }); return ( <div> {universes.map(universe => ( <div key={universe.id}>{universe.name}</div> ))} </div> ); }; const ErrorFallback = ({ error, resetErrorBoundary }) => ( <div role="alert"> <p>Something went wrong:</p> <pre>{error.message}</pre> <button onClick={resetErrorBoundary}>Try again</button> </div> ); const UniverseListWithSuspense = () => { return ( <ErrorBoundary FallbackComponent={ErrorFallback} onReset={() => window.location.reload()} > <Suspense fallback={<div>Loading universes...</div>}> <UniverseListContent /> </Suspense> </ErrorBoundary> ); }; export default UniverseListWithSuspense;

Por que ruim?

Código repetido, necessário para gerenciar estado assíncrono, se espalhará como ervas daninhas à medida que o projeto escala.

Se você não está disposto a usar React Query, pelo menos crie seus próprios hooks desacoplados e certifique-se de testá-los adequadamente.

cache

React Query pode cachear os resultados dos seus endpoints e expirá-los após um tempo de expiração configurável.

Também permite que você vincule tags com queries e as invalide em mutations.

refetch

Tão fácil quanto chamar uma função, como deveria ser.

const UniverseListContent = () => { const { data: universes, refetch } = useSuspenseQuery({ queryKey: ['universes'], queryFn: fetchUniverses, staleTime: FIVE_MINUTES, gcTime: TEN_MINUTES, retry: retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000), }); return ( <div> <button onClick={refetch}>Reload All</button> {universes.map(universe => ( <div key={universe.id}>{universe.name}</div> ))} </div> ); };