

React Suspense and Error Boundary
source link: http://neethack.com/2021/01/react-suspense-and-error-boundary/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

React Suspense and Error Boundary
TLDR: Suspend can catch Promise from children and render fallback until the promise is resolved.
In React 16.6, React is adding the Suspense
component that it can render fallback while the app is loading javascript or fetching API. You can see the demonstration from Dan Abramov’s presentation in React conf.
From the documentation on Reactjs webside, the example below:
function ProfilePage({ resource }) { return ( <Suspense fallback={<h1>Loading profile...</h1>}> <ProfileDetails resource={resource} /> <Suspense fallback={<h1>Loading posts...</h1>}> <ProfileTimeline resource={resource} /> </Suspense> </Suspense> ); }
Can render “Loading profile…” while ProfileDetails
is loading, and “Loading posts…” while ProfileTimeline
is loading. It can control the timing of render components, skip the children while loading, and avoid race conditions in children. However, it doesn’t just work like magic as the document described. Because for the Suspense component to work, the API needs to follow certain criteria.
How Suspense work is similar to the ErrorBoundary in React, for example:
<ErrorBoundary> <ProfileDetails resource={resource}/> </ErrorBoundary>
Can catch any errors thrown in the children and skip the render in children. Suspense is similar to ErrorBoundary, But instead of catching the error, it is catching Promise that is thrown from the children, render fallback while the promise is pending, and unblock the children when the promise is resolved.
To understand how it works, we can take a look at the source code of React.Lazy
, React.Lazy
can work with Suspense, wrapping javascript import
and trigger Suspense fallback while loading the component:
// Create dynamic loading component that trigger Suspense while loading. const LazyComponent = React.lazy(() => import('./Component));
<Suspense fallback={<h1>Loading component</h1>}> <LazyComponent> </Suspense>
A simplified version of React.lazy
source code looks like this:
const Unitialized = -1; const Pending = 0; const Resolved = 1; const Rejected = 2;
function lazy(ctor) { // hold component state in closure const payload = { _status: Unitialized, _result: ctor }
return { $$typeof: REACT_LAZY_TYPE, _payload: payload, _init: lazyInitializer } }
function lazyInitalizer(payload) { // import when first initialize if (payload._status === Uninitialized) { const thenable = payload._result(); // Transition to the Pending state. payload._status = Pending, payload._result = thenable
thenable.then( // save result to payload moduleObject => { if (payload._status === Pending) { // Transition to the Resolved state. payload._status = Resolved payload._result = moduleObject.default } }, error => { if (payload._status === Pending) { // Transition to the Rejected state. payload._status = Rejected; payload._result = error; } }, ); } else if (payload._status === Resolved) { // return result when resolved. return payload._result; } else { // throw Promise or Error when status is Pending or Rejected. throw payload._result; } }
Therefore for Suspense to work, the API needs to:
- Trigger
Promise
that loads the data - Throw the
Promise
while loading - Cache the result and return the result when the
Promise
is resolved.
Data Fetching
Let’s try to implement data fetching to support Suspense. We can reuse the concept in React.lazy
and replace the import
with fetch
const Unitialized = -1; const Pending = 0; const Resolved = 1; const Rejected = 2;
function suspenseFetch(url) { const payload = { _status: Unitialized, _result: () => fetch(url) }
return () => { if (payload._status === Uninitialized) { const promise = payload._result() payload._status = Pending payload._result = promise
promise.then((res) => { if (payload._status === Pending) { payload._status = Resolved payload._result = res } }, (err) => { if (payload._status === Pending) { payload._status = Rejected payload._result = err } }) } else if (payload._status === Resolved) { return payload._result } else { throw payload._result } } }
// in app const fetchUser = suspenseFetch(`/users/1`)
const User = () => { const user = fetchUser()
return <div>{user.name}</div> }
const App = () => ( <Suspense fallback={<h1>Loading user...</h1>}> <User> </Suspense> )
With the suspenseFetch
function above, we can convert fetch into a suspense compatible API.
use-async-resource is a package that can turns fetch into suspense compatible API too, with support for params and fetching the new result. It is a good resource if you want to implement the API with Suspense.
Conclusion
Suspense is an interesting concept that makes errors and async handling declarative, and it is supported on React level so it will be more stable and easy to handle in the future. However, the Apollo graphql client will not support Suspense API due to the usage of useRef
does not support throwing promises and errors. But we will see more libraries in React world support Suspense in the future.
Reference
Recommend
-
88
安全验证 - 知乎系统监测到您的网络环境存在异常,为保证您的正常访问,请点击下方验证按钮进行验证。在您验证完成前,该提示将多次出现
-
70
-
32
Building a Polyfill for React Suspense I chose this image for no other reason than be...
-
70
For bunpkg , I use a Wizard component to display a series of steps to generate UNPKG links. When a request to Web API fails, an error is caught...
-
19
So the React team just released a new API called Hooks . It’s amazing. It lets you declaratively model state and side effects. You’ve probably read about it elsewhere on the internet, so I’m not going to...
-
19
In this article, we are go to build a simple profile app with a combination of React.lazy(), Suspense and ErrorBoundary component. Don’t forget to check the complete working codes in the end of this page.
-
6
Not FoundYou just hit a route that doesn't exist... the sadness.LoginRadius empowers businesses to deliver a delightful customer experience and win customer trust. Using the LoginRadius Identity...
-
5
Error: Multipart: Boundary not found ReactJs & Axios
-
3
Suspense in React 18: How it works, and how you can use it React 18, also known as the Concurrent React, released earlier this year and brought with it important changes. The most i...
-
4
React error handling with react-error-boundary Jul 6, 2023 • Shruti Apte Shru...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK