Suspense in React
React Suspense is a powerful feature that allows developers to handle asynchronous rendering gracefully by displaying fallback UI while components or data are loading. It works hand-in-hand with React.lazy, Concurrent Rendering, and Data Fetching solutions like React Query, Relay, or custom implementations.
How Suspense Works
- Placeholder Fallback: Suspense wraps around components that need to wait for asynchronous operations. While waiting, it displays a fallback UI (e.g., a loading spinner).
- Automatic Rendering: Once the asynchronous operation completes, the real component or data is rendered.
Basic Syntax
import React, { Suspense } from "react";
const LazyComponent = React.lazy(() => import("./LazyComponent"));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}
export default App;
-
React.Suspense
: Wraps around lazy-loaded components. -
fallback
: Specifies the UI to show while waiting for the component to load.
Use Cases of Suspense
-
Lazy Loading Components: Loading components dynamically with
React.lazy
. - Data Fetching: Managing asynchronous data loading when combined with libraries like Relay or React Query.
- Concurrent Rendering: Optimizing rendering in concurrent React modes.
Example 1: Lazy Loading with Suspense
Without Suspense
import React from "react";
import HeavyComponent from "./HeavyComponent";
function App() {
return (
<div>
<h1>Main App</h1>
<HeavyComponent />
</div>
);
}
export default App;
The HeavyComponent
loads synchronously, increasing the initial load time.
With Suspense
import React, { Suspense } from "react";
const HeavyComponent = React.lazy(() => import("./HeavyComponent"));
function App() {
return (
<div>
<h1>Main App</h1>
<Suspense fallback={<div>Loading Heavy Component...</div>}>
<HeavyComponent />
</Suspense>
</div>
);
}
export default App;
Now, the HeavyComponent
loads only when required, showing a fallback during the loading process.
Example 2: Suspense with Multiple Lazy Components
import React, { Suspense } from "react";
const ComponentA = React.lazy(() => import("./ComponentA"));
const ComponentB = React.lazy(() => import("./ComponentB"));
function App() {
return (
<div>
<h1>Main App</h1>
<Suspense fallback={<div>Loading Components...</div>}>
<ComponentA />
<ComponentB />
</Suspense>
</div>
);
}
export default App;
The fallback UI is displayed until both components finish loading.
Example 3: Suspense for Data Fetching (Experimental)
React Suspense is also useful for asynchronous data fetching when integrated with libraries like Relay or React Query.
Example with React Query
import React, { Suspense } from "react";
import { useQuery } from "react-query";
function DataComponent() {
const { data } = useQuery("fetchData", fetchData);
return <div>{data}</div>;
}
function App() {
return (
<Suspense fallback={<div>Loading data...</div>}>
<DataComponent />
</Suspense>
);
}
export default App;
Error Handling with Suspense
If an error occurs during the loading of components or data, React Suspense does not provide built-in error handling. Use an Error Boundary for this purpose.
import React, { Suspense } from "react";
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return <div>Error loading component</div>;
}
return this.props.children;
}
}
const LazyComponent = React.lazy(() => import("./LazyComponent"));
function App() {
return (
<ErrorBoundary>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</ErrorBoundary>
);
}
export default App;
Best Practices for Using Suspense
- Simple Fallbacks: Use lightweight placeholders to avoid performance overhead.
- Combine with Error Boundaries: Ensure proper handling of component or data load failures.
- Chunk Intelligently: Divide your application into logical chunks for better performance.
Benefits of Suspense
- Improved User Experience: Ensures the app remains responsive by showing a fallback UI.
- Reduced Bundle Size: Works well with code splitting for optimized bundle sizes.
- Simplified Asynchronous Handling: Manages loading states in a more declarative way.
Advanced Use: Nested Suspense
You can nest multiple Suspense components for granular control over fallback states.
import React, { Suspense } from "react";
const ComponentA = React.lazy(() => import("./ComponentA"));
const ComponentB = React.lazy(() => import("./ComponentB"));
function App() {
return (
<div>
<h1>Main App</h1>
<Suspense fallback={<div>Loading Component A...</div>}>
<ComponentA />
</Suspense>
<Suspense fallback={<div>Loading Component B...</div>}>
<ComponentB />
</Suspense>
</div>
);
}
export default App;
Limitations of Suspense
- Data Fetching Support: Experimental feature for fetching data directly.
- No Error Handling: Requires additional error handling mechanisms like Error Boundaries.
- Browser Compatibility: Requires modern browsers with support for ES6 modules and promises.
Conclusion
React Suspense is a versatile tool for managing asynchronous rendering in React applications. It simplifies handling loading states, enhances performance with lazy loading, and creates a smoother user experience when combined with code splitting and error handling techniques.
Top comments (0)