React is an open source JavaScript library that developers use to create Web and mobile based applications and supports building interactive user interfaces and UI components. Created by Facebook software engineer Jordan Walke, the first version of React came out seven years ago and is now maintained by Facebook. Since the React Framework was first released, React’s popularity has skyrocketed. In October 2020, React 17 was released, but with a surprise — “zero new features.” Of course, that doesn’t really mean there aren’t any new features added to excite programmers. In fact, this release brought us a number of major feature upgrades, bug fixes for version 16, and the introduction of: Concurrent Mode and Suspense. Although these two features have not been officially released, they have been made available to developers for testing. Once released, they will change the way React presents its UI, doubling performance and user experience.

Concurrent Mode and Suspse allow users to seamlessly handle data loading, loading states, and user interface operations for smoother and seamless switching. In Concurrent Mode, React can pause the rendering of costly, non-urgent components and focus on more urgent tasks such as UI rendering, keeping the application responsive and avoiding white screens, stalling, and so on.

This article focuses on sharing an in-depth understanding of data extraction capabilities under Concurrent Mode and Suspense modes.

Why is Concurrent Mode needed?

As we all know, JavaScript frameworks or libraries are single-threaded jobs. Thus, while one block of code runs, the rest must wait for execution. Unable to perform multithreaded work concurrently. The same goes for interface rendering. Once React starts rendering something, you can’t interrupt it until it’s finished. The React developers call this rendering “blocking rendering.” This blocking rendering creates an unstable user interface and can stop responding at any time.

Specific problems

Let’s say we need to display a long list of optional applications for filtering the product. We use a search box to filter records. The design is that when the user clicks the search button, the user interface needs to refresh to list the associated data.

If the list is too long and there is too much data, the UI “stags,” that is, renders visible to the user. This kind of lag can also greatly reduce product performance. There are techniques that developers can use, such as throttling and buffering, that can help, but are not a perfect solution. Throttling limits the number of times a particular function can be called. By using throttling, we can avoid repeated calls to expensive and time-consuming APIs or functions. This process can improve performance, especially in the presentation of information in the user interface.

Jitters will ignore calls to functions for a predetermined amount of time. Function calls occur only after a predetermined amount of time has elapsed.

The figure below illustrates the stalling phenomenon: UI stalling while waiting for a non-urgent API call to complete, thereby preventing the rendering of the user interface. The solution is to use concurrent mode for interruptible rendering.

Uninterrupted rendering

With interruptible rendering, React.js does not block the UI while processing and re-rendering lists. It makes React.js more refined by pausing trivial work, updating the DOM, and making sure the UI doesn’t stall. React uses user input to update or redraw input boxes in parallel. React uses user input and redraws the input field in parallel. It also updates the list in memory. After React completes the update, it updates the DOM and re-renders the list on the user’s monitor. Essentially, uninterrupted rendering enables React to “multitask”. This feature provides a smoother UI experience.

Concurrent mode

Concurrent mode is a set of features that help the React application stay responsive and smoothly adapt to the user’s device and network speed capabilities. Concurrent mode divides the tasks it owns into smaller chunks. The React scheduler picks and selects jobs to execute. The scheduling of jobs depends on their priority. By prioritizing tasks, it can stop trivial or non-urgent things, or push them further. React always puts user interface updates and rendering first.

Using concurrent mode, we can:

  • Control the first rendering process
  • Prioritize rendering
  • Pause and resume rendering of components
  • Cache and optimize runtime rendering of components
  • Hide the display until it is needed

With UI rendering, concurrent mode improves the response to incoming data, lazy loading of controls, and asynchronous processing. Concurrent mode ensures that the user interface is always active and updating data in the background. Concurrent mode also uses two React hooks: UseTransition and UseDeferredValue

Using useDeferredValue Hook

UseDeferredValue Hook is defined as follows:

const deferredValue = useDeferredValue(value, { timeoutMs: <some value> });

This command sets the value to “lag” after the time set in Timeoutms. Whether the user interface must be updated immediately or must wait for data, this command keeps the user interface active and responsive, this Hook avoids UI lags and always keeps the user interface responsive to keep the cost of getting data lags small.

Use the Transition Hook

UseTransition hooks are React hooks that are mainly used to suspend in a scenario where there is a web page with a username button. At the click of a button, the page displays the user’s details on the screen. Assume that the user clicks one button first, and then the next. The screen either goes blank, or we see a spinner on the screen. If it takes too long to get the details, the user interface may freeze. The useTransition method returns the values of two hooks: startTransition and isPending. The syntax for the definition is as follows:

const [startTransition, isPending] = useTransition({ timeoutMs: 3000 });

The syntax defined by startTransition:

<button disabled={isPending} startTransition(() => { <fetch Calls> }); </button> {isPending? " Loading..." : null}

Using the UseTransition hook, React.js continues to display the user interface without user details until the user details are ready, but the UI is responsive. React prioritizes the user interface to maintain responsiveness while retrieving data in parallel.

Suspense for getting data

Suspense is another experimental feature introduced by React along with the concurrency pattern. Suspense enables components to wait a predetermined period of time before rendering. Suspense’s main purpose is to read data asynchronously from a component without worrying about the source of the data. Suspense fits best with the concept of delayed loading. Suspense allows the data acquisition library to notify React whether a data component is available or not. React does not update the UI until the necessary components are in place.

Benefits of using Suspense:

1. Integration between the data acquisition library and the React component

2. Control the visual loading state

Avoid competitive conditions

The basic syntax for the Spinner component is as follows:


import Spinner from './Spinner';  
    <Suspense fallback={<Spinner />}>  
      <SomeComponent />  
</Suspense>

Suspense, used in Concurrent Mode, allows time-consuming components to start rendering while waiting for data. Display placeholders at the same time. This combination results in a smoother UI experience.

Suspense and Lazy Load Components

Reaction. lazy is a new feature that enables Reaction. js to lazily load components. Lazy loading means that components are loaded only when needed (the code that retrieves and renders them). They prioritize the most critical user interface components. The React developers suggest wrapping the lazy loading component in the Suspense component. This ensures that the component does not render in a “bad state.” The user interface remains responsive throughout the process and leads to a smoother user experience.

Enable Concurrency Mode

To enable concurrent mode, install the latest test version. The prerequisite for installing React is the Node Packet Manager (NPM). To install the test version, execute the following command:


npm install react@experimental react-dom@experimental

To test whether the test version is set, create a sample React application. The render code without the test function is as follows:

import * as React from 'react';  
  import { render } from 'react-dom';  
  render(<App />, document.getElementById('root'));  

Concurrency mode, the specific code is as follows:


import * as React from 'react';  
    import { createRoot } from 'react-dom';  
createRoot(document.getElementById('root')).render(<App />);

This will enable concurrency mode for the entire application. React divides the render call into two parts:

  1. Creating the root element
  2. Using Render Calls

Currently, React plans to maintain three modes:

  1. Traditional schemas are backward compatible traditional or current schemas
  2. Blocking mode is an intermediate stage in the development of concurrent patterns
  3. Concurrent mode

Blocking mode is the replacement of the createRoot call with the createBlockingRoot call. In the case of concurrent mode development, blocking mode provides the developer with the opportunity to fix bugs or resolve problems.

The React official documentation also explains what each mode supports:

Sample application:

This article also created a test program to verify the use and effect of the concurrency pattern and other patterns. Taking pixel application as an example, this paper randomly distributes pixels on a 150*150 canvas and includes a search box. Every time the user clicks on the search box, the canvas will re-render itself. Even if the UI interface cannot render in concurrent mode, user input does not stop updating. The pixel canvas is rerendered after processing. In traditional mode, the UI stops when you type quickly, sometimes before rendering the canvas again. User input will also stop and will not be updated.

The main file to build pixel applications is Canvas. Js. We also made an input box where the user could type anything. Pixel Canvas is rerendered each time a key is pressed.

Code example:

  • Index.js

import React from "react";  
    import ReactDOM from "react-dom";  
    import App from "./App";  
    // Traditional or non-Concurrent Mode react  
    const rootTraditional = document.getElementById("root");  
    ReactDOM.render(<App caption="Traditional or Block Rendering" />,  
    rootTraditional);  
    // Concurrent Mode enabled  
    const rootConcurrent = document.getElementById("root-concurrent");  
    ReactDOM.createRoot(rootConcurrent).render(<App caption="Interruptible  
Rendering"   />);
  • App.js

import React, { useState, useDeferredValue } from "react";  
  import "./App.css";  
  import { Canvas } from "./Canvas";  
  export default function App(props)  
  { const [value, setValue] = useState("");  

 //This is available only in the Concurrent mode.

    const deferredValue = useDeferredValue(value, {  
      timeoutMs: 5000  
    });  

    const keyPressHandler = e => {  
      setValue(e.target.value);  
    };  

    return (  
      <div className="App">  
        <h1>{props.caption}</h1>  
        <input onKeyUp={keyPressHandler} />  
        <Canvas value={deferredValue} />  
      </div>  
    );  
  }
  
  • Canvas.js
import React from "react";  
   const CANVAS_SIZE = 70;  
   const generateRandomColor = () => {  
    var letters = "0123456789ABCDEF";  
    var color = "#";  
    for (var i = 0; i < 6; i++) {  
      color += letters[Math.floor(Math.random() * 16)];  
    }  
    return color;  
  };  
   const createCanvas = (rows, columns) => {  
    let array = [];  
    for (let i = 0; i < rows; i++) {  
      let row = [];  
      for (let j = 0; j < columns; j++) {  
        row.push(0);  
      }  
      array.push(row);  
    }  
    return array;  
  };  
   //This is the square with the pixels  
  const drawCanvas = value => {  
    const canvas = createCanvas(CANVAS_SIZE, CANVAS_SIZE);  
    return canvas.map((row, rowIndex) => {  
      let cellsArrJSX = row.map((cell, cellIndex) => {  
        let key = rowIndex + "-" + cellIndex;  
        return (  
         <div  
            style={{ backgroundColor: generateRandomColor() }}  
            className="cell"  
            key={"cell-" + key}  
          />  
        );  
      });  
      return (  
        <div key={"row-" + rowIndex} className="canvas-row">  
          {cellsArrJSX}  
        </div>  
      );  
    });  
  };  
  export const Canvas = ({ value }) => {  
    return (  
     <div>  
        <h2 style={{ minHeight: 30 }}>{value}</h2>  
       <div className="canvas">{drawCanvas(value)}</div>  
     </div>  
   );  
 };

  • Index.html
<! DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <meta name="theme-color" content="#000000" /> <title>React App Concurrent Mode</title> </head> <body> <noscript> You need to enable JavaScript to run this app. </noscript> <div id="container"> <div id="root" class="column"></div> <div id="root-concurrent" class="column"></div> </div> </body> </html>

Run the example

Let’s look at our code. The first screen we see is the initial screen. Using traditional or block rendering is what React does now. Interruptible rendering is a test function for concurrent mode. Let’s take a look at traditional rendering work first.

The pixel canvas is rerendered with each keystroke. In traditional rendering, the entire UI is paused at each keystroke until it can re-render the screen. During this time, user input will not be updated even if we continue typing.

The following figure shows interruptible rendering. In interruptible rendering, the user can continue typing. The UI does not stop or stop when the canvas is re-rendered in parallel for each keystroke.

After the renderings are complete, React updates the UI. Although it’s hard to see in a static screenshot, we can see that the grid is changing, but the user can still type without the UI stuttering.

conclusion

In this article, we studied React’s test concurrency capabilities and Suspense. Using concurrent mode, React.js always keeps the user interface responsive. It breaks down the application’s tasks into smaller chunks and allows the prioritization of user interface tasks. As a result, this mode provides a smoother and seamless user experience and improves the overall performance of the application.

Combined with the concurrency pattern, Suspense allows the user interface to remain responsive. At the same time, heavy and time-consuming tasks such as data acquisition can be done in parallel, providing an overall seamless experience.

Full details on concurrency patterns can be found in the React official documentation.

With the React version improved, the React framework is becoming more and more familiar to Chinese front-end developers and widely used in their projects. Js is a popular front-end framework that supports integration with the React framework, such as the ActiveReportsJS front-end report control, SpreadJS pure front-end report control, etc. Provide an online editor and report design tool that integrates directly with React to improve the front end data presentation functionality.

SpreadJS is a pure front-end table control

SpreadJS is an HTML5-based pure front-end spreadsheet control that can be natively embedded in a wide variety of applications and integrated with front-end technology frameworks. Integrate SpreadJS with React for Excel like spreadsheets within the React framework, including support for more than 450 formulas, online import and export of Excel documents, PivotTables, and multiple data visualizations. Make the application program have extremely high data processing performance and response speed.