React Concurrent Mode - Revisited

Understanding React Concurrent Mode & The Problems It Solves

React Concurrent Mode - Revisited

Concurrent Mode was announced about an year ago and still is an experimental feature which lets our app remain responsive in different network conditions and based on different device capabilities. Without any further ado, let's discuss the new features and concepts that are introduced in Concurrent Mode. We will discuss these features in context of React and React apps:

1. Interruptible Rendering 

Normally in React, if rendering begins and in between any user event occurs (like clicking a button), React will first complete the previous render and then process the user event. So, React uses blocking rendering which will create some performance issues.

Concurrent Mode introduces the concept of interruptible rendering, so if any rendering is started and an user event happens in between, React will stop the previous rendering and first process the user event, then continue to complete the remaining part of previous rendering. Concurrent Mode basically uses Time Slicing for interruptible rendering. 

2. Data Fetching With Suspense

It works on Render as you Fetch approach, which means that React will start loading the data without waiting for the component to get loaded. Both the data and component start loading parallelly. 

Here, we wrapped our MySuspenseImage Component in a Suspense Component which basically provides the fallback component until the image gets loaded on the screen. 

Here, we try to render the MySuspenseImage even when imageData is not loaded. It will make MySuspenseImage component to suspend as promise is thrown. The Suspense component catches the promise and shows the fallback component for the time being and as soon as the promise resolves, it will re-render the image component. If any errors occur during loading the data, they can be handled by using the ErrorBoundary component.

3. SuspenseList

Suppose that there are lots of API calls in a page. It is quite possible that their responses will not come in a particular order as required by the UI, so it is not good for user experience. We can handle this problem by chaining promises in a particular order but it still requires some complex logic. SuspenseList handles this problem for us by providing a proper loading sequence for different parts of a page. 

SuspenseList Component basically wraps different Suspense Component whose order of revelation can be handled by using the revealOrder property. The order can be 'forwards', 'backwards' and 'together'.

We can also control the number of loading indicators by using tail property. It can be 'hidden' and 'collapsed'. When tail is collapsed, we will see only one loader at a time.

We can see it here in the gif below that where revealOrder is 'forwards' and tail is 'collapsed'. First, the user image is rendered, then user details and finally the user comments are rendered on the screen. In this way, we can use suspense to handle the loading sequence of our app. All the asynchronous processes can start at the same time & only the reveal order on screen can be decided by the SuspenseList Component.

4. useTransition hook

Suppose you are on a fast connection and you click on your profile. What happens is that you will instantly transition to the new screen and may see loaders for very small amount of time (say 200ms). This introduces the problem of flickering in UI. We can handle this by using some complex logic, but useTransition hook makes our work simple.It let us wait on the previous screen for some data to load or wait for given time and then make the transition to the new screen. 

useTransition hook returns two values : 
    1. startTransition() : It handles the state update we want to defer.
    2. isPending : A boolean which shows that the data is still loading.

You can see in the above diagram that when a user clicks, an event for transition occurs. React will start preparing the update on a different branch and wait on the previous branch for the new screen data to get loaded for a given timeout (say 200ms).

If the data loaded quickly, then the given time it will transition to the new screen, otherwise it will wait on the previous screen for the given timeout. Then, the branches will merge and we see data on the new screen. It helps us avoid the jarring loading states of the app and makes UI transitions much smoother. 

It is visible that the loader is shown on the previous screen for the userDetails to get loaded for given timeout and then transitions to the new screen and loads the remaining data, hence avoiding jarring screen animations.

You can check out this little project of mine at GitHub. The link is shared below. If there are any questions or doubts, feel free to leave a comment at the GitHub repo and I'll get back to you.

Github project link :


Thank you for reading!

2020 © All rights reserved. GeekyAnts India Pvt Ltd.