use é uma API do React 19 que lê um valor de uma promise ou de um context enquanto um componente renderiza. use(promise) pausa o componente até a promise resolver, mostrando enquanto isso o fallback do <Suspense> mais próximo; use(context) funciona como o useContext, mas pode ficar dentro de um if ou de um loop.
A função fetchUser abaixo faz o papel de uma requisição real.
A primeira renderização mostra "Loading..." por um segundo e depois o perfil. Clique em User 2: o fallback volta enquanto a nova promise está pendente. Profile não tem estado de loading próprio; ele é escrito como se os dados já estivessem ali.
Como o use(promise) funciona
Quando Profile chama use(userPromise):
- Se a promise já resolveu, o
useretorna o valor dela e a renderização continua. - Se ela ainda está pendente, o React para de renderizar
Profilee mostra o fallback do<Suspense>mais próximo acima dele. Quando a promise se resolve, o React renderizaProfilede novo, e desta vez ouseretorna o valor. - Se ela rejeitou, o React mostra o error boundary mais próximo.
O React guarda o resultado no próprio objeto da promise. É por isso que a promise precisa ser o mesmo objeto na próxima renderização.
Para manter o perfil antigo na tela enquanto o próximo carrega, defina a promise dentro de uma transição. O React então espera em vez de mostrar o fallback de novo:
<button onClick={() => startTransition(() => setUserPromise(fetchUser(2)))}>User 2</button>
Onde criar a promise
É aqui que as pessoas tropeçam. Escrever a chamada de busca dentro do componente que a lê parece natural e não funciona:
// Do not do this
function Profile({ id }) {
const user = use(fetchUser(id)); // a new promise on every render
return <p>{user.name}</p>;
}
Cada renderização chama fetchUser e recebe uma promise nova, pendente. Profile fica suspenso. Quando essa promise resolve, o React renderiza Profile de novo, o que cria outra promise pendente, que suspende de novo. O componente nunca mostra os dados, e a aba de rede se enche de requisições.
Crie a promise em algum lugar que não execute de novo a cada renderização de quem a lê:
- Em um pai, guardada no estado (como no primeiro exemplo) ou criada em um event handler.
- No nível do módulo, quando os dados não dependem de props:
const configPromise = fetchConfig();acima do componente. - Em um cache indexado pelos argumentos, para que o mesmo id retorne a mesma promise.
- Em um framework: um Server Component pode passar uma promise para um componente do cliente como prop, e os loaders de rotas fazem o mesmo. Esse é o caso para o qual o
usefoi projetado.
Aqui está a versão com cache, para que um componente possa pedir dados por id e ainda receber uma promise estável:
Passe pelas três cidades e depois volte a uma que você já abriu. O console registra uma requisição por cidade, e uma cidade que você já viu aparece sem o fallback, porque a promise dela já resolveu. Apague a verificação if (!cache.has(city)) e o componente volta a ficar suspenso para sempre.
Um cache real também precisa de um jeito de expirar entradas. Bibliotecas de dados e frameworks cuidam disso, e é por isso que a maioria dos apps recebe as promises de um deles.
use(context), até dentro de um if
use(SomeContext) retorna o mesmo valor que useContext(SomeContext). A diferença é onde você pode chamá-lo. Todos os outros hooks precisam executar no nível superior, na mesma ordem em toda renderização (as regras dos hooks). O use pode ser chamado depois de um retorno antecipado, dentro de uma condição ou em um loop.
Clique no botão para alternar entre os dois ramos. Troque use(UserContext) por useContext(UserContext) e o código quebra as regras dos hooks, já que o hook executaria em algumas renderizações e em outras não.
O use ainda tem limites: chame-o só durante a renderização de um componente ou dentro de um hook personalizado. Em um event handler ou em um efeito, leia o context com um valor que você já obteve durante a renderização.
Erros: error boundaries, não try/catch
Quando a promise rejeita, o erro vai para o error boundary mais próximo, do mesmo jeito que um erro de renderização.
Abra o Post 2 e o boundary mostra a mensagem de erro. Abra o Post 1 de novo e funciona: key={id} dá ao boundary um estado novo para cada post, então ele não continua mostrando o erro antigo.
Você não consegue capturar um use pendente ou rejeitado com try/catch. Enquanto a promise está pendente, o use interrompe a renderização lançando um valor especial, e um catch em volta dele o engoliria. O valor capturado é um erro cuja mensagem começa com "Suspense Exception: This is not a real error!", e em desenvolvimento o React também registra "use was called from inside a try/catch block". Para mostrar um valor alternativo em vez de uma tela de erro, trate a rejeição na promise antes de passá-la para baixo:
const postPromise = fetchPost(id).catch(() => 'This post could not be loaded.');
use comparado com a busca em useEffect
O padrão clássico no cliente busca em um efeito e mantém três partes de estado:
function Profile({ id }) {
const [user, setUser] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
let ignore = false;
fetchUser(id).then(
(u) => !ignore && setUser(u),
(e) => !ignore && setError(e),
);
return () => {
ignore = true;
};
}, [id]);
if (error) return <p>Error</p>;
if (!user) return <p>Loading...</p>;
return <p>{user.name}</p>;
}
useEffect | use | |
|---|---|---|
| Quando a requisição começa | depois que a primeira renderização está na tela | quando a promise é criada, possivelmente antes de o componente renderizar |
| Estado de loading | o seu próprio if (!user) | o <Suspense> mais próximo |
| Erros | o seu próprio estado error | o error boundary mais próximo |
| Condições de corrida | você se protege delas (ignore) | o componente sempre lê a promise que recebeu |
| Quem cria a promise | o componente | um pai, um cache ou um framework |
A versão com efeito é autocontida, e é por isso que ela ainda é a abordagem mais comum em apps sem framework; a página sobre buscar dados trata dela por completo. O use brilha quando algo acima do componente inicia a requisição cedo e entrega a promise para baixo.
Perguntas frequentes
O que o hook use faz no React?
use(resource) retorna o valor de uma promise ou de um context. Com uma promise, o componente fica suspenso até ela resolver e o <Suspense> mais próximo mostra o fallback; se ela rejeitar, aparece o error boundary mais próximo.
O use pode ser chamado condicionalmente?
Sim. O use é o único hook que você pode chamar dentro de instruções if, de loops e depois de um return antecipado. Ele ainda precisa ser chamado em um componente ou em um hook, nunca em um event handler ou em um efeito.
Por que use(fetch()) dentro do meu componente entra em loop infinito?
Cada renderização cria uma nova promise. O componente fica suspenso nela, o React renderiza de novo quando ela resolve e essa renderização cria outra promise pendente. Crie a promise fora da renderização: no estado de um pai, no nível do módulo, em um cache ou em um loader de framework.
Posso envolver o use em try/catch?
Não. Quando uma promise está pendente, o use interrompe a renderização lançando um valor, e capturá-lo quebra esse mecanismo. Trate uma promise rejeitada com um error boundary, ou adicione .catch() à promise para que ela resolva com um valor alternativo.
Devo trocar a busca de dados com useEffect pelo use?
O use precisa de uma promise criada uma vez e reutilizada, que é o que frameworks e bibliotecas de dados oferecem. Em um app client-side comum, buscar em um efeito (ou com uma biblioteca como TanStack Query) continua comum e adequado.