Hook use() - Lee promesas y valores de contexto con soporte condicional
Receta
import { use, Suspense } from "react";
// Leyendo una promesa -- debe estar envuelta en Suspense
function UserProfile({ userPromise }: { userPromise: Promise<{ name: string; email: string }> }) {
const user = use(userPromise);
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
// Leyendo contexto -- reemplaza useContext
const ThemeContext = React.createContext<"light" | "dark">("light");
function ThemedButton() {
const theme = use(ThemeContext);
return <button className={theme === "dark" ? "bg-gray-800 text-white" : "bg-white"}>Haz clic en mí</button>;
}
// Uso
function App() {
const userPromise = fetchUser(1); // crea la promesa fuera del renderizado, o en un Server Component
return (
<ThemeContext value="dark">
<Suspense fallback={<p>Cargando usuario...</p>}>
<UserProfile userPromise={userPromise} />
</Suspense>
<ThemedButton />
</ThemeContext>
);
}Cuándo usarlo: Usa use() para leer una promesa pasada desde un Server Component o componente padre, o para leer condicionalmente contexto en ramas donde useContext no puede ser llamado.
Ejemplo funcional
// Un panel que carga condicionalmente diferentes datos según el rol del usuario
"use client";
import { use, Suspense, createContext } from "react";
type User = { id: string; name: string; role: "admin" | "viewer" };
type Stats = { revenue: number; users: number; orders: number };
type Activity = { id: string; description: string; timestamp: string };
const AuthContext = createContext<User | null>(null);
function DashboardContent({
statsPromise,
activityPromise,
}: {
statsPromise: Promise<Stats>;
activityPromise: Promise<Activity[]>;
}) {
const user = use(AuthContext);
if (!user) {
return <p>Por favor, inicia sesión.</p>;
}
// use() puede ser llamado condicionalmente -- esto es único entre los hooks de React
if (user.role === "admin") {
const stats = use(statsPromise);
return (
<div>
<h2>Panel de administración</h2>
<dl>
<dt>Ingresos</dt><dd>${stats.revenue.toLocaleString()}</dd>
<dt>Usuarios</dt><dd>{stats.users.toLocaleString()}</dd>
<dt>Pedidos</dt><dd>{stats.orders.toLocaleString()}</dd>
</dl>
</div>
);
}
const activity = use(activityPromise);
return (
<div>
<h2>Feed de actividad</h2>
<ul>
{activity.map((a) => (
<li key={a.id}>
{a.description} <time>{a.timestamp}</time>
</li>
))}
</ul>
</div>
);
}
export default function Dashboard({
user,
statsPromise,
activityPromise,
}: {
user: User | null;
statsPromise: Promise<Stats>;
activityPromise: Promise<Activity[]>;
}) {
return (
<AuthContext value={user}>
<Suspense fallback={<p>Cargando panel...</p>}>
<DashboardContent
statsPromise={statsPromise}
activityPromise={activityPromise}
/>
</Suspense>
</AuthContext>
);
}Lo que esto demuestra:
use(AuthContext)leyendo contexto sinuseContextuse(promise)llamado condicionalmente dentro de un bloqueif-- imposible con hooks tradicionales- Diferentes promesas resueltas dependiendo del rol del usuario
- Límite Suspense capturando el estado pendiente mientras las promesas se resuelven
Análisis profundo
Cómo funciona
use()no es un hook en el sentido tradicional. No sigue las "reglas de hooks" -- puedes llamarlo dentro de bucles, condicionales y returns tempranos. Esto es lo que lo distingue deuseContext.- Cuando se llama con una Promesa,
use()suspende el componente hasta que la promesa se resuelva. El límite<Suspense>más cercano muestra el fallback. Si la promesa se rechaza, el límite de error más cercano captura el error. - Cuando se llama con un Contexto,
use()se comporta idénticamente auseContext()-- lee el valor actual del proveedor más cercano arriba en el árbol. - La promesa que pasas a
use()debe ser estable -- no crees una nueva promesa en cada renderizado. Las promesas deben venir desde Server Components, estar cacheadas o ser creadas fuera de la función de renderizado. use()se integra con las características concurrentes de React. Mientras una promesa está pendiente, React puede continuar renderizando otras partes del árbol.
Variaciones
Server Component pasando una promesa a un Client Component:
// app/page.tsx (Server Component)
import UserCard from "./UserCard";
export default function Page() {
// Comienza a obtener pero NO esperes -- deja que el cliente lo resuelva
const userPromise = fetchUser(1);
return <UserCard userPromise={userPromise} />;
}// app/UserCard.tsx
"use client";
import { use } from "react";
export default function UserCard({ userPromise }: { userPromise: Promise<{ name: string }> }) {
const user = use(userPromise);
return <h2>{user.name}</h2>;
}Lectura condicional de contexto:
import { use, createContext } from "react";
const FeatureFlagContext = createContext<Set<string>>(new Set());
function FeatureGate({ flag, children }: { flag: string; children: React.ReactNode }) {
const flags = use(FeatureFlagContext);
if (!flags.has(flag)) return null;
return <>{children}</>;
}Encadenando promesas:
function OrderDetails({ orderPromise }: { orderPromise: Promise<{ id: string; userId: string }> }) {
const order = use(orderPromise);
// Puedes usar el valor resuelto para crear otra promesa
// pero cachéala para evitar recrearla en cada renderizado
const userPromise = useMemo(() => fetchUser(order.userId), [order.userId]);
const user = use(userPromise);
return (
<div>
<p>Pedido #{order.id}</p>
<p>Cliente: {user.name}</p>
</div>
);
}Notas de TypeScript
use<T>(promise: Promise<T>): T-- El tipo resuelto se infiere de la promesa.use<T>(context: React.Context<T>): T-- El tipo del valor del contexto se infiere.- Cuando el tipo de promesa es complejo, anota la prop:
userPromise: Promise<User>. use()se importa desde"react":import { use } from "react".
Trampas
- Crear promesas durante el renderizado -- Llamar a
fetch()onew Promise()dentro del cuerpo del componente crea una nueva promesa en cada renderizado, causando bucles de suspense infinitos. Solución: Crea la promesa en un Server Component, componente padre o usauseMemo/cachepara estabilizarla. - Límite Suspense faltante -- Si no existe un ancestro
<Suspense>cuandouse()lee una promesa, React lanza un error. Solución: Envuelve el componente (o un ancestro) en<Suspense fallback={...}>. - Límite de error faltante -- Si la promesa se rechaza y no hay límite de error, el error se propaga a la raíz. Solución: Añade un límite de error arriba de los componentes que usan
use()con promesas. - use() no puede ser llamado con todos los tipos de recursos -- Solo
PromiseyContextson soportados. Los Observables, generadores e iterables asincronos no lo son. Solución: Convierte otras fuentes asincronos en promesas antes de pasarlas ause(). - Context.Provider está deprecado -- Cuando usas
use(MyContext), ten en cuenta que<MyContext.Provider>sigue funcionando pero la sintaxis preferida es<MyContext value={v}>. Solución: Migra a la sintaxis más corta. - No es una solución completa de obtención de datos --
use()no cachea, deduplica o revalida. Lee una única promesa. Solución: Empareja con una capa de cacheo (Reactcache(), cacheo a nivel de framework, o una biblioteca como SWR/TanStack Query).
Alternativas
| Enfoque | Cuándo elegir |
|---|---|
use(promise) | Leyendo una promesa desde un Server Component o fuente cacheada |
useContext(ctx) | Lectura tradicional de contexto (sigue funcionando, pero no puede ser llamado condicionalmente) |
use(context) | Lectura condicional de contexto en ramas |
| TanStack Query / SWR | Obtención completa de datos con cacheo, revalidación, paginación |
useEffect + useState | Patrón heredado de obtención al montar (sigue funcionando pero menos ergonómico) |
React.lazy | División de código de componentes (caso de uso diferente al de promesas de datos) |
Preguntas frecuentes
¿Cómo difiere use() de los hooks tradiciones de React como useContext?
use()puede ser llamado dentro de bucles, condicionales y returns tempranos -- no sigue las "reglas de hooks"- Acepta tanto Promesas como objetos Context, mientras que
useContextsolo funciona con Context use()es el reemplazo preferido parauseContext()en adelante
¿Qué sucede cuando pasas una promesa a use() sin un límite Suspense?
- React lanza un error si no existe un ancestro
<Suspense>cuandouse()lee una promesa pendiente - Siempre envuelve el componente (o un ancestro) en
<Suspense fallback={...}> - Si la promesa se rechaza, el límite de error más cercano captura el error
¿Puedes llamar a use() condicionalmente dentro de un componente?
Sí. Este es el diferenciador clave con respecto a otros hooks:
function Dashboard({ user, statsPromise, activityPromise }) {
if (user.role === "admin") {
const stats = use(statsPromise);
return <AdminView stats={stats} />;
}
const activity = use(activityPromise);
return <ActivityFeed activity={activity} />;
}¿Por qué debes evitar crear promesas durante el renderizado?
- Llamar a
fetch()onew Promise()dentro del cuerpo del componente crea una nueva promesa en cada renderizado - Esto causa bucles de suspense infinitos porque React ve una nueva promesa pendiente cada vez
- Crea promesas en un Server Component, componente padre o estabiliza con
useMemo/cache
¿Cómo pasas una promesa desde un Server Component a un Client Component?
// Server Component -- NO esperes
export default function Page() {
const userPromise = fetchUser(1);
return <UserCard userPromise={userPromise} />;
}
// Client Component
"use client";
import { use } from "react";
export default function UserCard({ userPromise }) {
const user = use(userPromise);
return <h2>{user.name}</h2>;
}¿Es use() una solución completa de obtención de datos como TanStack Query o SWR?
- No.
use()no cachea, deduplica ni revalida datos - Lee una única promesa e se integra con Suspense
- Empareja con una capa de cacheo (React
cache(), cacheo de framework, o una biblioteca) para necesidades de obtención de datos en producción
¿Cómo encadenas obtenciones de datos dependientes con use()?
function OrderDetails({ orderPromise }) {
const order = use(orderPromise);
const userPromise = useMemo(
() => fetchUser(order.userId),
[order.userId]
);
const user = use(userPromise);
return <p>Pedido #{order.id} por {user.name}</p>;
}Usa useMemo para estabilizar la promesa dependiente y evitar la recreación en cada renderizado.
Trampa: Context.Provider sigue funcionando pero ¿cuál es la sintaxis preferida en React 19?
<MyContext.Provider value={v}>está deprecado- Usa
<MyContext value={v}>directamente como el JSX del proveedor - Tanto
use(MyContext)comouseContext(MyContext)leen de cualquiera de las dos sintaxis
Trampa: ¿Qué tipos de recursos acepta use()?
- Solo
PromiseyReact.Contextson soportados - Observables, generadores, iterables asincronos y otras fuentes asincronos no son soportadas
- Convierte otras fuentes asincronos en promesas antes de pasarlas a
use()
¿Cómo está tipado use() en TypeScript para promesas vs contexto?
// Promesa -- tipo resuelto inferido
const user: User = use<User>(userPromise);
// Contexto -- tipo de valor inferido
const theme: "light" | "dark" = use(ThemeContext);Importa desde "react": import { use } from "react".
¿Cómo tipas una prop de componente que acepta una promesa para use() en TypeScript?
type Props = {
dataPromise: Promise<{ id: string; name: string }>;
};
function MyComponent({ dataPromise }: Props) {
const data = use(dataPromise);
return <p>{data.name}</p>;
}Relacionado
- Server Components -- Donde típicamente se crean y pasan las promesas
- Form Actions -- useActionState para estado asincrónico relacionado con formularios
- Overview -- Lista completa de características de React 19