Nwose Lotanna Web Developer and Writer

What’s new in React v17, and the road to v18

5 min read 1532

What's New in React v17, and the Road to v18

A few weeks ago, the React team released the library’s latest version, React v17.0 RC. In this post, we will look into the new changes and updates this new release shipped with.

React is a declarative, efficient, and flexible JavaScript library for building user interfaces, and it’s inarguably one of the most popular JavaScript libraries around. It has more than 156,000 stars on GitHub and one of the most vibrant frontend communities building great applications.

UX for upgrading versions and the road to v18

This new version is unique in that it does not ship with any visible new features. Instead, it is fully focusing on the upgrade experience of React as a whole.

Since React came into existence, there has always been a new version setup process, wherein you have to choose whether to upgrade your version to use new features or continue using older versions, without any in-between. For instance, the Context API has been deprecated, and even though most React apps do not use it, React still supports it.

There have been ongoing debates around continuing support or leaving behind apps that use old versions. So moving forward, this upgrade strategy was re-imagined to be more inclusive of older versions, and as such, the new version 17 enables gradual React upgrades.

The React team basically tried to ensure that, moving forward, upgrading from one React version to another is easy and seamless. With version 17, you get a stepping stone to ensure that, for example, it is even safer to embed a tree you manage in one version inside another tree managed by an entirely different version.

This is simply about providing more upgrading options because before now, if you upgraded from one React version to a newer one, it would upgrade the whole app. In some instances where you can have two versions of React in the same project, it causes a lot of issues with events.

When React 18 comes out, for instance, upgrading from a version like 17 will give you options for both a full app upgrade or a gradual one. If you choose the gradual, your app will be upgraded piece by piece — for example, upgrading most of an app to React 18 and keeping a dialog component on version 17.

This may not be the ideal route, but it will come in handy for developers that maintain large projects, and this new version will ensure these apps are no longer being left behind.

Changes to event delegation

React processes event handlers in such a way that if you put one inline on a button like this:

We made a custom demo for .
No really. Click here to check it out.

<button onClick={handleClick}>

The vanilla JS equivalent on compile will look like this:

myButton.addEventListener('click', handleClick);

React then attaches one handler per event type to the document node directly, as opposed to attaching it to the DOM nodes on which they were declared. This is called event delegation, and it is very beneficial for large apps and processes like replaying of events.

Considering the gradual upgrades we just talked about in the last section, nesting apps built with different React versions also had to be reimagined. In this new version of React, event handlers will no longer be attached at the document level; rather, they will be attached to the DOM container where the tree was rendered.

const rootNode = document.getElementById('root');
ReactDOM.render(<App />, rootNode);

With this change in place, it is now quite safe to nest apps built with different versions of React, although this will start from version 17 and above.

Breaking changes

There are a few breaking changes that shipped with this new version. The team ensured they are as minimal as possible; some of them are just behavioral but still important to note. So things like deprecated methods in previous releases are still available.

Event delegation

You might potentially experience some issues with the new changes around event delegation.

For instance, if you add manual listeners with document.addEventListener() you would normally expect that it will catch all React events. In previous React versions, even if you called a stop propagation function in an event handler, your custom document listeners will still get them because the native event is already at the level of the document.

In this new version, the e.stopPropagation() will actually stop your document handler from releasing:

document.addEventListener('click', function() {
  // This custom handler will no longer receive clicks
  // from React components that called e.stopPropagation()

To fix this, convert your event listener to use a capture phrase by passing a { capture: true } option as the third argument, like so:

document.addEventListener('click', function() {
  // Now this event handler uses the capture phase,
  // so it receives *all* click events below!
}, { capture: true });

With this, we see that event delegation is now closer to the normal DOM than ever before.

Browser alignment

A few changes have been made to the event system in React, some of which include:

  • To prevent common confusions like firing when scrolling through child elements, the onScroll event no longer bubbles
  • The React onBlur and onFocus events have now switched to using native focusin and focusout events internally, better matching React’s existing behavior and even providing more information
  • Capture phrase events like onClickCapture now make use of actual browser capture phrase listeners

These few changes align React more closely with how browsers behave and even improve interoperability.

No event pooling

Starting from this new version, event pooling optimization has been removed from React due to continued confusion and the simple fact that does not improve modern browser performance.

function handleChange(e) {
  setData(data => ({
    // This crashes in React 16 and earlier:
    text: e.target.value

The React team calls this a behavior change and has labeled it breaking, although they have not seen it break anything at Facebook, so the chances are really low. Also, e.persist() is still available on the event objects, although it does not do anything.

Effect cleanup timing

This new version also makes the useEffect Hook cleanup function timing more consistent.

useEffect(() => {
  // This is the effect itself.
  return () => {    // This is its cleanup.  };});

In React 16, the effect cleanup function is run synchronously, as opposed to most effects, which do not delay screen updates and which React runs asynchronously by default. The React team has found that this synchronous process is not so ideal, just like it isn’t with componentWillMount for large apps when a user switches tabs.

So this new version brings some changes. The effect cleanup function will now run asynchronously like the others, and if the component is un-mounting, the cleanup will just run after updates are displayed on the screen.

Consistent errors for returning undefined

React developers know that functions that return undefined are flagged with an error:

function Button() {
  return; // Error: Nothing was returned from render

Mostly because of how easy it is to return undefined unintentionally:

function Button() {
  // We forgot to write return, so this component returns undefined.
  // React surfaces this as an error instead of ignoring it.
  <button />;

Initially, this behavior was only exclusive to class and function components, but with this new version, forwardRef and memo components have now been added, making their behavior consistent with regular class and function components.

let Button = forwardRef(() => {
  // We forgot to write return, so this component returns undefined.
  // React 17 surfaces this as an error instead of ignoring it.
  <button />;
let Button = memo(() => {
  // We forgot to write return, so this component returns undefined.
  // React 17 surfaces this as an error instead of ignoring it.
  <button />;

Note that for instances in which you would render nothing intentionally, n``u``ll will be returned instead.

Removing private exports

Some React internals have been exposed to other unrelated projects especially the React Native for the web which used to depend on internals from the event system which was very fragile and broke easily. Now these private exports are now removed in this new React version a different solution has been put in place for them that stops them from depending on these internals.

This change just means that older RN for web versions are not going to be compatible with React v17 but newer versions will.

How to upgrade

You are advised to use this version for test projects, as more stable versions are coming in the near future. You can raise any issues you might encounter while using or upgrading to use React v17 RC here on GitHub.

To install with npm, run:

npm install react@17.0.0-rc.1 react-dom@17.0.0-rc.1

To install with Yarn, run:

yarn add react@17.0.0-rc.1 react-dom@17.0.0-rc.1

The team also has UMD builds of React through a CDN:

<script crossorigin src="https://unpkg.com/react@17.0.0-rc.1/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17.0.0-rc.1/umd/react-dom.production.min.js"></script>

You can read the docs for detailed installation instructions.


And with that, we come to the conclusion of our review. Though React 17 did not come with any front-facing new features, it sets a precedent for version 18 by directly addressing the upgrade experience and more closely aligns React’s behavior with modern browsers. Happy hacking!

You come here a lot! We hope you enjoy the LogRocket blog. Could you fill out a survey about what you want us to write about?

    Which of these topics are you most interested in?
    ReactVueAngularNew frameworks
    Do you spend a lot of time reproducing errors in your apps?
    Which, if any, do you think would help you reproduce errors more effectively?
    A solution to see exactly what a user did to trigger an errorProactive monitoring which automatically surfaces issuesHaving a support team triage issues more efficiently
    Thanks! Interested to hear how LogRocket can improve your bug fixing processes? Leave your email:

    Full visibility into production React apps

    Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

    LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

    The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

    Modernize how you debug your React apps — .

    Nwose Lotanna Web Developer and Writer

    Leave a Reply