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.
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.
React processes event handlers in such a way that if you put one inline on a button like this:
<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.
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.
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.
A few changes have been made to the event system in React, some of which include:
onScroll
event no longer bubblesonBlur
and onFocus
events have now switched to using native focusin
and focusout
events internally, better matching React’s existing behavior and even providing more informationonClickCapture
now make use of actual browser capture phrase listenersThese few changes align React more closely with how browsers behave and even improve interoperability.
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 => ({ ...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.
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.
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.
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.
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 [email protected] [email protected]
To install with Yarn, run:
yarn add [email protected] [email protected]
The team also has UMD builds of React through a CDN:
<script crossorigin src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/[email protected]/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!
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.