React.js, part 5: the use() hook and reading a promise in render
Part 5from the React.js series · 20 parts in all
For years the rule was absolute: data fetching belongs in an effect, render must be pure.
The use hook bends that rule in a controlled way — it lets a client component
read a promise (or a context) during render, and cooperates with Suspense instead of
guessing. Part 5 covers what it does, and the discipline it still requires.
use(promise): unwrap a value the server already started
use suspends the component until the promise resolves, then returns its
value. The promise must be stable across renders — a new promise every
render is an infinite loop — so it is normally created by a server component or a cache and
passed down:
'use client';
import { use } from 'react';
export function Comments({ commentsPromise }) {
// Reading the promise is a render-time unwrap, not an effect.
// While it is pending, the nearest <Suspense> shows its fallback.
const comments = use(commentsPromise);
return <ul>{comments.map(c => <li key={c.id}>{c.text}</li>)}</ul>;
}
// Server component: start the promise, hand the CLIENT the promise itself.
export default function Article({ id }) {
const commentsPromise = getComments(id); // do not await
return (
<Suspense fallback={<Spinner />}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
);
}
Notice the shape: the server starts the work early, the client unwraps it where it is needed. That is how RSC streams data without a client-side fetch at all.
use(context): context without the hook caveats
use also reads context, and unlike useContext it may be called
conditionally — inside an if or after an early return — which finally makes
"optional provider" patterns comfortable:
function Themed({ invert }) {
if (!invert) return <Plain />;
const theme = use(ThemeContext); // legal: use() is not a hook-position API
return <div className={theme}>...</div>;
}
What use() does not change
Components still must be pure; use is a read, not a fetch trigger. Creating
the promise is still someone else's job — a server component, a route loader, or a cache
keyed by the request. The mental shift is that "loading state" is expressed as a Suspense
boundary, not an isLoading boolean. Next: pulling external, non-React data into
React safely with useSyncExternalStore.