Suspense
fetch-on-render
function ProfilePage() {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser().then(u => setUser(u));
}, []);
if (user === null) {
return <p>Loading profile...</p>;
}
return (
<>
<h1>{user.name}</h1>
<ProfileTimeline />
</>
);
}
function ProfileTimeline() {
const [posts, setPosts] = useState(null);
useEffect(() => {
fetchPosts().then(p => setPosts(p));
}, []);
if (posts === null) {
return <h2>Loading posts...</h2>;
}
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.text}</li>
))}
</ul>
);
}
- Start fetching user details
- Wait…
- Finish fetching user details
- Start fetching posts
- Wait…
- Finish fetching posts
fetch-then-render
// Kick off fetching as early as possible
const promise = fetchProfileData();
function ProfilePage() {
const [user, setUser] = useState(null);
const [posts, setPosts] = useState(null);
useEffect(() => {
promise.then(data => {
setUser(data.user);
setPosts(data.posts);
});
}, []);
if (user === null) {
return <p>Loading profile...</p>;
}
return (
<>
<h1>{user.name}</h1>
<ProfileTimeline posts={posts} />
</>
);
}
// The child doesn't trigger fetching anymore
function ProfileTimeline({ posts }) {
if (posts === null) {
return <h2>Loading posts...</h2>;
}
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.text}</li>
))}
</ul>
);
}
- Start fetching user details
- Start fetching posts
- Wait…
- Finish fetching user details
- Finish fetching posts
Render-as-You-Fetch
// This is not a Promise. It's a special object from our Suspense integration.
const resource = fetchProfileData();
function ProfilePage() {
return (
<Suspense fallback={<h1>Loading profile...</h1>}>
<ProfileDetails />
<Suspense fallback={<h1>Loading posts...</h1>}>
<ProfileTimeline />
</Suspense>
</Suspense>
);
}
function ProfileDetails() {
// Try to read user info, although it might not have loaded yet
const user = resource.user.read();
return <h1>{user.name}</h1>;
}
function ProfileTimeline() {
// Try to read posts, although they might not have loaded yet
const posts = resource.posts.read();
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.text}</li>
))}
</ul>
);
}
- Render
<ProfileDetails>
- It calls
resource.user.read()
- None of the data is fetched yet, so this component “suspends”
- React skips over it, and tries rendering other components
- Render
<ProfileTimeline>
- It calls
resource.posts.read()
- No data yet, so this component also “suspends”
- React skips over it too, and tries rendering other components
- Nothing left to try rendering
<ProfileDetails>
suspended, React shows the closest <Suspense>
fallback: <h1>Loading profile...</h1>
- Done for now.
What is concurrent mode?
useDeferredValue
SuspenseList
useTransition
Recoil
Leave a Reply