Home
React
Advanced Hook: use()
Daniel Nguyen
Daniel Nguyen
July 11, 2025
1 min

React provides a declarative approach to async rendering through Suspense, Error Boundaries, and the use() API.

1. Suspense

Suspense displays fallback UI while a child component is waiting for a resource supported by Suspense.

import { Suspense } from 'react'
function App() {
return (
<Suspense fallback={<div>Loading phone details...</div>}>
<PhoneDetails />
</Suspense>
)
}

When PhoneDetails suspends, React displays the fallback and retries rendering when the resource becomes available.

Important: Suspense does not automatically detect ordinary fetch() calls or manage every loading state.

2. Error Boundaries

Error Boundaries display fallback UI when a descendant throws an error during rendering.

import { ErrorBoundary } from 'react-error-boundary'
import { Suspense } from 'react'
function App() {
return (
<ErrorBoundary
fallback={<div>Oops, something went wrong.</div>}
>
<Suspense fallback={<div>Loading...</div>}>
<PhoneDetails />
</Suspense>
</ErrorBoundary>
)
}
  • Suspense handles pending resources.
  • ErrorBoundary handles rendering errors.
  • A recovery UI can let users retry or navigate away.

3. The use() API

React 19 introduced use() for reading resources such as Promises during rendering.

import { use } from 'react'
function PhoneDetails({
phonePromise,
}: {
phonePromise: Promise<{ name: string }>
}) {
const phone = use(phonePromise)
return <div>{phone.name}</div>
}

If the Promise is pending, the component suspends. If it rejects, React can propagate the error to the nearest Error Boundary.

Unlike ordinary Hooks, use() can be called inside conditions and loops, subject to React’s rules.

Key distinction: use() reads a Promise; it does not initiate fetching or cache the result.

4. Keep Promises Stable

Avoid creating a new Promise on every render:

function ProfileDetails({ username }: { username: string }) {
const favorites = use(getFavorites(username))
return <div>{favorites.length} favorites</div>
}

If getFavorites() creates a new Promise each time, repeated suspension may occur.

Instead, create the Promise in a supported data-fetching layer or cache it by key:

const userCache = new Map<string, Promise<User>>()
function getUserPromise(id: string) {
let promise = userCache.get(id)
if (!promise) {
promise = fetch(`/api/user/${id}`).then((res) => {
if (!res.ok) {
throw new Error('Failed to fetch user')
}
return res.json() as Promise<User>
})
userCache.set(id, promise)
}
return promise
}

This is a simplified example. Production caches should also consider rejected Promises, retries, invalidation, and user-specific data isolation.

5. Avoid Async Waterfalls

Consider two independent requests:

function ProfileDetails({ username }: Props) {
const favorites = use(getFavorites(username))
const friends = use(getFriends(username))
return <Profile favorites={favorites} friends={friends} />
}

If each function starts a request, rendering may suspend on the first Promise before starting the second request. Recreating Promises on retries can make this even worse.

Start both requests before rendering the component that consumes them:

function ProfilePage({ username }: Props) {
const favoritesPromise = getFavorites(username)
const friendsPromise = getFriends(username)
return (
<Suspense fallback={<div>Loading profile...</div>}>
<ProfileDetails
favoritesPromise={favoritesPromise}
friendsPromise={friendsPromise}
/>
</Suspense>
)
}
function ProfileDetails({
favoritesPromise,
friendsPromise,
}: {
favoritesPromise: Promise<Favorite[]>
friendsPromise: Promise<Friend[]>
}) {
const favorites = use(favoritesPromise)
const friends = use(friendsPromise)
return <Profile favorites={favorites} friends={friends} />
}

Both requests start before ProfileDetails reads either Promise. In production, ensure the parent or data-fetching layer preserves Promise identity across rerenders.


Tags

#ReactAdvancedHooks

Share

Daniel Nguyen

Daniel Nguyen

Frontend Developer

Frontend developer specializing in React, Next.js, and JavaScript. Writing practical guides on modern web development at Dev98.

Expertise

React
Next.js
JavaScript
TypeScript
Python

Social Media

githublinkedinyoutubewebsite

Related Posts

Advanced Hook
Advanced Hook: useSyncExternalStore()
July 19, 2025
1 min
Dev98

Dev98

React · Next.js · Web development