DEV Community

Tianya School
Tianya School

Posted on

React Suspense and Concurrent Mode-The Future of Asynchronous Rendering

React’s Suspense and Concurrent Mode, introduced in React 16.8 and later, aim to enhance user experience and performance, particularly for asynchronous data loading and animations. They are part of React’s next-generation rendering strategies, designed to deliver smoother interactions and more efficient resource scheduling.

Suspense

Suspense is a component that allows you to designate an area where components may load asynchronously. When the data for these components isn’t ready, Suspense displays a placeholder (fallback) until the data is available, then renders the component. Here’s a simple example:

Purpose

Suspense primarily addresses asynchronous data loading during component rendering, enabling components to wait for their dependencies to be ready before rendering, rather than showing incomplete placeholders or error messages.

How It Works

  • Asynchronous Boundary: The Suspense component acts as a boundary, wrapping child components that may need to wait for data to load.
  • Fallback UI: During the wait, Suspense uses a fallback prop to display a loading indicator or other placeholder content.
  • Data Preloading: Combined with React.lazy, it enables lazy loading of components, automatically triggering their load on first render.
  • Data Loading Coordination: Paired with React’s Context API and hooks (e.g., useSuspenseResource), it provides fine-grained control over data loading.
import React, { useState, useEffect, lazy, Suspense } from 'react';
import { fetchSomeData } from './asyncDataFetch'; // Asynchronous data fetch function

const AsyncComponent = lazy(() => {
  return new Promise((resolve) => {
    fetchSomeData().then(() => resolve(import('./AsyncComponent')));
  });
});

function App() {
  const [dataReady, setDataReady] = useState(false);

  useEffect(() => {
    fetchSomeData().then(() => setDataReady(true));
  }, []);

  return (
    <div>
      {dataReady ? (
        <Suspense fallback={<div>Loading...</div>}>
          <AsyncComponent />
        </Suspense>
      ) : null}
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

In this code, AsyncComponent is lazily loaded. It renders only when fetchSomeData completes and dataReady is set to true; otherwise, a “Loading…” placeholder is shown.

Concurrent Mode

Concurrent Mode is a rendering strategy that allows React to pause and resume rendering without interrupting the user interface. By intelligently scheduling tasks, it optimizes user experience, such as pausing background content loading to prioritize rendering visible parts during user interactions like scrolling.

Purpose

Concurrent Mode enhances application responsiveness and interaction smoothness through concurrent rendering and intelligent scheduling. It allows React to use idle time efficiently for UI updates while ensuring immediate responses to high-priority tasks.

Core Concepts

  • Concurrent Rendering: Enables multiple rendering tasks to run simultaneously, allowing React to pause low-priority renders for user inputs or high-priority updates.
  • Time Slicing: Breaks complex rendering tasks into smaller chunks, executed incrementally to avoid blocking the main thread.
  • Priority Scheduling: React assigns rendering priorities based on task urgency (e.g., user interactions).
import React, { useState, useEffect, startTransition } from 'react';

function MyComponent() {
  const [value, setValue] = useState(0);

  useEffect(() => {
    startTransition(() => {
      // Code here runs in a concurrent task, not blocking UI updates
      setValue(value + 1);
    });
  }, [value]);

  return <div>{value}</div>;
}

export default MyComponent;
Enter fullscreen mode Exit fullscreen mode

In this example, startTransition places the code in a low-priority task, ensuring it doesn’t block ongoing UI updates even if it takes time to execute.

Combining Suspense and Concurrent Mode

Suspense and Concurrent Mode are often used together to create smoother application experiences, allowing asynchronous operations to proceed without interrupting the UI.

import React, { useState, useEffect, startTransition, lazy, Suspense } from 'react';
import { fetchSomeData } from './asyncDataFetch'; // Asynchronous data fetch function

const AsyncComponent = lazy(() => {
  return new Promise((resolve) => {
    fetchSomeData().then(() => resolve(import('./AsyncComponent')));
  });
});

function App() {
  const [dataReady, setDataReady] = useState(false);

  useEffect(() => {
    startTransition(() => {
      fetchSomeData().then(() => setDataReady(true));
    });
  }, []);

  return (
    <div>
      {dataReady ? (
        <Suspense fallback={<div>Loading...</div>}>
          <AsyncComponent />
        </Suspense>
      ) : null}
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

startTransition ensures data loading doesn’t block the UI, while Suspense displays a loading indicator until the data is ready. Together, they provide a seamless user experience even during asynchronous data and component loading.

Practical Benefits

1. Efficient Resource Loading and Rendering

  • Lazy Loading: Using React.lazy and Suspense, components can be loaded on-demand, reducing initial load times and improving user experience.
  • Data Preloading: Preload data before users reach a page or state, minimizing wait times during interactions.

2. Graceful Error Handling

  • Unified Error Display: Combine Error Boundaries with Suspense’s error handling to manage errors during component loading or data fetching consistently.

3. Dynamic Priority Adjustment

  • Adaptive User Experience: Concurrent Mode allows React to adjust rendering task priorities based on the runtime environment (e.g., device performance, user interaction state) for optimal performance.

4. Simplified State Management

  • Seamless Integration with State Libraries: When used with MobX, Redux, or React’s Context API, Suspense and Concurrent Mode streamline asynchronous state updates, reducing synchronization complexity.

5. Future Scalability

  • Framework-Level Support: As React evolves, Suspense and Concurrent Mode will unlock further potential, such as improved support for server-side rendering (SSR) and client-side rendering (CSR), plus a broader API set for flexible rendering logic control.

Complete Example of Suspense and Concurrent Mode

Install Required Libraries

npm install axios react-spring react-dom
Enter fullscreen mode Exit fullscreen mode

Create a simple component that displays an animation after data loading:

import React, { lazy, Suspense, useState, useEffect } from 'react';
import { useSpring, animated } from 'react-spring';
import axios from 'axios';

const LazyAnimatedComponent = lazy(() => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(import('./LazyAnimatedComponent'));
    }, 2000); // Simulate async loading delay
  });
});

function App() {
  const [isLoaded, setIsLoaded] = useState(false);
  const fadeInProps = useSpring({ opacity: isLoaded ? 1 : 0 });

  useEffect(() => {
    axios.get('https://api.example.com/data').then(() => {
      setIsLoaded(true);
    });
  }, []);

  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <animated.div style={fadeInProps}>
          <LazyAnimatedComponent />
        </animated.div>
      </Suspense>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

In LazyAnimatedComponent, add animation effects, such as a fade-in:

import React from 'react';
import { animated, useSpring } from 'react spring';

function LazyAnimatedComponent() {
  const fadeInProps = useSpring({ opacity: 1 });

  return (
    <animated.div style={fadeInProps}>
      <h1>Hello, World!</h1>
      <p>This is an animated lazy-loaded component.</p>
    </animated.div>
  );
}

export default LazyAnimatedComponent;
Enter fullscreen mode Exit fullscreen mode

To fully leverage Concurrent Mode, enable it in the ReactDOM rendering method, typically at the entry point for server-side and client-side rendering:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

// Client-side rendering
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
Enter fullscreen mode Exit fullscreen mode

In this example, we check for server-side rendered HTML and use createRoot for client-side rendering. This approach ensures Suspense and Concurrent Mode benefits are utilized even with server-side rendering.

📘 *Want to get more practical programming tutorials? *

👨‍💻 If you want to systematically learn front-end, back-end, algorithms, and architecture design, I continue to update content packages on Patreon
🎁 I have compiled a complete series of advanced programming collections on Patreon:

  • Weekly updated technical tutorials and project practice
  • High-quality programming course PDF downloads
  • Front-end / Back-end / Full Stack / Architecture Learning Collection
  • Subscriber Exclusive Communication Group

👉 Click to join and systematically improve development capabilities: patreon.com/tianyaschool

Thank you for your support and attention ❤️

Top comments (0)