use API: The Evolution of useContext and Handling Promises (Suspense)

Chris is building a complex dashboard component.
This component renders differently based on user settings: 'Normal Mode' and 'Advanced Mode'. He wants to access a specific Context (AdvancedContext) to fetch data only when in Advanced Mode.
So, Chris wrote the code intuitively.
// ❌ Chris's Mistake: Hooks cannot be used inside conditional statements.
function Dashboard({ isAdvanced }) {
if (isAdvanced) {
// Error: React Hooks must be called in the exact same order...
const advancedData = useContext(AdvancedContext);
return <AdvancedView data={advancedData} />;
}
return <SimpleView />;
}The moment he pressed save, React spewed a red error: "Hook rule violation."
React Hooks are not magic; they are an order-based system relying on array indices. Therefore, they cannot be called inside loops, conditions, or nested functions.
Eventually, Chris had to pull useContext up to the top level, even though the data wasn't needed when isAdvanced was false.
However, in React 19, this restriction is lifted. Thanks to the arrival of the new use API, which behaves like a hook but isn't one.
1. Consuming Context Flexibly
The use API looks similar to React Hooks, but there is a decisive difference: It can be called inside conditional statements and loops.
Now Chris doesn't have to forcefully hoist the hook. He can simply "read" the Context when and where it is needed.
// ✅ React 19: Using the use API
import { use } from 'react';
function Dashboard({ isAdvanced }) {
if (isAdvanced) {
// Reading Context inside a conditional statement is allowed!
const advancedData = use(AdvancedContext);
return <AdvancedView data={advancedData} />;
}
return <SimpleView />;
}use can completely replace useContext. Now, without interrupting the flow of component logic, you can fetch Context values naturally following the data flow. This increases the code's Cohesion.
2. Unwrapping Promises: Async like Sync
The true value of the use API shines when handling Promises (Async tasks).
Until now, to fetch asynchronous data, we had to combine useEffect and useState or rely on libraries like TanStack Query. It required a complex process of managing isLoading state while fetching and rendering once the data arrived.
However, the use API can accept a Promise object directly as an argument. And it Suspends React rendering until that Promise is Resolved.
Using Promises in Client Components
Imagine fetching data in a Server Component (RSC) and passing that Promise to a Client Component (Message).
// ClientComponent.tsx
import { use, Suspense } from 'react';
function Message({ messagePromise }: { messagePromise: Promise<string> }) {
// 1. Waits until the Promise is resolved. (Pauses here)
// 2. Returns the result (string) once resolved.
const messageContent = use(messagePromise);
return <p>{messageContent}</p>;
}
export function MessageContainer({ promise }) {
return (
// 3. The parent Suspense handles the UI to show while waiting.
<Suspense fallback={<p>Downloading message...</p>}>
<Message messagePromise={promise} />
</Suspense>
);
}Looking at this code, the await keyword is nowhere to be seen. Yet, asynchronous data is stored in messageContent just like a synchronous variable.
This is the "Suspense-based Data Fetching" that React pursues.
3. Principle: A New Mental Model for 'Reading' Values
The use API is not a function for "Fetching" data, but for "Reading" data.
This pattern is similar to useSuspenseQuery we learned in Part 2. But now, you can write "Declarative code without loading states" using only React's native features without external libraries.
4. Cautions and Limitations
The use API is not a silver bullet. There are a few rules to follow.
1. Usable Only During Rendering
use must only be called during the render phase of a component or hook. It cannot be used inside event handlers (onClick) or useEffect. (Just use await there.)
2. Server Components vs. Client Components
3. Caching is Separate
use is merely a tool for reading data; it does not cache data or prevent duplicate requests. To cache API requests, you still need to use TanStack Query or your framework's (Next.js) caching features.
Key Takeaways
Context and async handling have become this elegant. Then how has the biggest headache, "Form Handling," changed?
It's time to be liberated from the grunt work of manually creating isSubmitting, error, and result states with useState.
Continuing in: "useActionState & useFormStatus: The Revolution of Form Management."