Learn about concurrent mode in React, and how this set of features will help to create more fluid and responsive applications.
React is one of the best JavaScript libraries to create applications nowadays, and we can list a lot of factors that are responsible for this achievement: the concepts (virtual DOM), the excellent team behind the technology, the very engaged community, etc.
All of these factors make a difference in the success and adoption of a JavaScript library. That’s why React is always building new features, to improve the experience of its developers and users—including the topic of today’s post, concurrent mode, a set of features that’s been hyped and highly anticipated by many React developers.
Although announced over a year ago at React Conf 2019, concurrent mode is still experimental. So if you haven’t yet taken the chance to explore it, let’s find out more about this set of features called concurrent mode and the benefits it can bring to our applications.
There’s no other factor that makes as big a difference in modern applications as performance. How your application is delivered and rendered, how the components are communicating with each other—it all drives the final result.
JavaScript is a single-threaded language, which means that it has one call stack and one memory heap. To execute the code, JavaScript executes in order and must finish one piece of code to move to the next one. Sometimes this synchronicity can be very harmful to performance, especially in React.
Imagine that we have an input field and a list of items. In this input field, we can search for items and return them to this list of items. Every time we type a new key inside the input field, we’ll trigger a re-render inside the list of items. This situation might not be a problem in a small list of items, but imagine that instead of a small list of items, you have 10 thousand items and you’ll trigger a re-render every time you type a new key.
Triggering unnecessary re-renders can cause you a lag in performance—that’s why you have some techniques to prevent this behavior. Throttling and debouncing in JavaScript are techniques that can prevent unnecessary API calls, loads and, in React applications, re-renders.
Throttling will call a function only once after a specific amount of time. It reduces drastically the frequency of function calls. Debouncing delays the process of the event for a predetermined amount of time.
You might have worked with or noticed these methods in many applications—they’re quite popular nowadays. Both techniques can prevent our UI from re-rendering over and over again, and can, if implemented correctly, significantly improve performance.
These techniques are very useful, especially in React, but we should be looking for better alternatives—especially at this moment when JavaScript is a very mature programming language with amazing features that were not available in the past.
That’s why the React team started the development of a new set of features to allow us to create more responsive and fluid applications, and they called it concurrent mode.
Concurrent mode in React is a set of features that is going to help us have more fluid and responsive UIs by allowing us to prevent render-blocking updates, and to start to have interruptible rendering, letting us prioritize render updates.
React today works under the render-blocking approach. Once a new rendering is started, we cannot stop or go back while it’s rendering. So the other render updates need to wait until this render update is finished. This can cause a lot of problems and is one of the use cases to use throttling and debouncing in React applications.
Concurrent mode fixes this problem by having an interruptible-render approach. This means that once a render update is started, it can be stopped and a higher priority update can run, and then return to update what it was rendering earlier.
This is really a fantastic feature for React—imagine all the possibilities we have and future applications we can build with it. The performance of the application can improve, the user’s experience will be way smoother than before, and also the developer’s experience will be much better.
To enable concurrent mode in your React application, you need to use the experimental builds of React.
If you’re using create-react-app
, it always is installed with the latest stable version of React.
To try concurrent mode in your project, all you need to do is install the experimental:
yarn upgrade react@experimental react-dom@experimental
After installing the experimental build of React, like other features such as fragments and hooks, you need to enable it before starting to use it. To enable concurrent mode in your whole application, you need to change the ReactDOM.render()
for ReactDOM.createRoot()
.
import ReactDOM from 'react-dom';
const root = document.getElementById('root');
ReactDOM.createRoot(root).render(<App />);
Although it’s experimental and a set of features in development, concurrent mode is being used by Facebook in production now. But there’s a reason why they’re using it in production: they’re actually there to fix possible bugs and breaking changes.
So, it’s very recommended to not use concurrent mode in production yet—since we don’t have it in a stable release and it is still in development, it can cause unexpected bugs and breaking changes.
One of the nice features of concurrent mode is Suspense. Suspense allows us to wait for some code to load, and, while it’s loading, we can display anything we want—a loading spinner, for example.
To fetch data nowadays, we have a very common and default way to do it in React, and this is how a lot of applications are working when trying to fetch data from their APIs: Make a request to fetch data; while it’s fetching, render something to the user, something similar to a loading spinner component. When the fetch is done, render the data to the user.
This common method can quickly turn into a problem if we do not do it carefully. A lot of developers like to use a boolean state like isLoading
to check if the data is ready or not. To solve that problem, concurrent mode offers Suspense.
Suspense allows a component to suspend the render while a condition is not met, and while this condition is not met, it can render a fallback component. Pretty similar to an isLoading
boolean state, but it can interrupt the rendering of a component and be more responsive and fluid.
We have now Suspense available to use—it’s been available since React 16.6—but since Suspense is a part of concurrent mode, it’s not recommended to use it in production.
But let’s create an example to test how Suspense works We’ll create a new create-react-app and
create a small example of calling a public API. In this example, we’ll use the PokeAPI.
We’ll create a simple component called Pokemon.js
, which is going to receive a name
prop and display this name
prop.
import React from 'react';
const Pokemon = ({ _name_ }) => {
return <h4>{_name_}</h4>;
}
export default Pokemon;
Now, inside our App.js
component, we’ll import the Pokemon.js
component that we just created using the lazy
function from React.
Inside our App.js we will make a call to the PokeAPI and return 50 results, and save it to our state.
const [results, setResults] = useState([]);
useEffect(() => {
axios
.get("https://pokeapi.co/api/v2/pokemon?limit=50")
.then(_res_ => setResults(_res_.data.results))
}, [])
Now we will use Suspense. Let’s import it from React, and wrap our whole App.js
component using the Suspense
component. As the fallback component, we’ll display a simple h1
element.
return (
<_Suspense_ _fallback_={<h1>Loading...</h1>}>
...
</_Suspense_>
);
Now, we’ll map our results
state, and we’ll render each result with our Pokemon.js
component, wrapping it inside another Suspense
component.
return (
<_Suspense_ _fallback_={<h1>Loading...</h1>}>
{results.map(({ _name_ }) => (
<_Suspense_ _key_={_name_} _fallback_={<h1>Loading...</h1>}>
<_Pokemon_ _name_={_name_} />
</_Suspense_>
))}
</_Suspense_>
);
We have now a small example of Suspense working pretty fine. You can notice that while we’re fetching the data, the h1
element is rendered, and after the data is ready, it renders the Pokemon component.
Concurrent mode for React is a very powerful set of features. We can totally see the benefits that it will bring to our applications, allowing us to have more responsive and fluid applications, prioritizing render updates and increasing the user’s performance.
In this article, we learned more about the concurrent mode in React and the benefits that this set of features will bring to React. We also learned about Suspense, a feature that allows us to manage loading states while resolving async requests.
Leonardo is a full-stack developer, working with everything React-related, and loves to write about React and GraphQL to help developers. He also created the 33 JavaScript Concepts.