Concurrent Mode requires a stricter way of writing components

Daishi Kato
Mar 7 · 3 min read

Introduction

According to the React 16.x Roadmap, we can expect Concurrent Mode soon.

React 16.x (~Q2 2019): The One with Concurrent Mode

Concurrent Mode allows React apps to be more responsive by rendering component trees without blocking the main thread.

It is opt-in and allows React to interrupt a long-running render (for example, rendering a new feed story) to handle a high-priority event (for example, text input or hover).

Concurrent Mode also improves the user experience of Suspense by skipping unnecessary loading states on fast connections.

While it is an opt-in feature, you can enable it easily and if your component isn’t properly implemented, it won’t work correctly.

In short, you can’t make side effects in your render function. This has always been true, but it hasn’t been a real issue until we got Concurrent Mode.

In Concurrent Mode, render functions could be invoked multiple times without actually committing (meaning, for example, applying changes to the DOM).

Luckily, Strict Mode intentionally invokes render functions twice, and you can see the wrong behavior in development mode. Refer the doc for more information.

This short article focuses on useRef, one of React’s Hooks. The useRef Hook is pretty powerful and can often be misused. In general, developers should avoid using useRef if they could use useState instead.

This article shows example code that uses useRef incorrectly and how to fix it.

The example is a simple counter just to illustrate the issue. It’s not product code, and you can actually implement the same example with useState.


Bad Code

It works as expected in traditional React, where the render phase and the commit phase is one-to-one.

However, if it invokes the render function multiple times without committing, the count increases unexpectedly.


Good Code

This code uses useEffect, whose first argument function is only invoked in the commit phase.

The currentCount is a local variable within the render function scope, and it will only change the ref count in the commit phase. The ref is essentially a global variable outside the function scope, hence modifying it is a side effect.


The Demo

To run the two code examples above, here’s the app component.

We don’t, in fact, need ConcurrentMode, just StrictMode is enough.

Please check out the following “codesandbox” to see the actual behavior.


Final notes

The reason I want to use useRef is to develop a bindings library for Redux. It requires the subscription to the global store, and to update components when the state is updated.

The ref is used to keep track of the last rendered state. For more information, check out the GitHub repository.

Better Programming

Advice for programmers.

Daishi Kato

Written by

A freelance programmer. I’m interested in working remotely with people abroad: https://contact.axlight.com https://github.com/dai-shi

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade