React.js 17: No New Features and New JSX Transform
When React.js 17 became available on October 20th, it generated some confusion because it has no new developer-facing features.
Instead, the React Core team focused on making the React library easier to self-upgrade in the future. This means, that if your app is running React 17 and you need to upgrade some features to React 18, you can move your application core to React 18 and lazily load the legacy code without any issues.
Of course, you can still upgrade your entire app the new version at once, which is the recommended version by the React team.
Installation
To install React 17 with npm, run:
npm install react@17.0.0 react-dom@17.0.0
To install React 17 with Yarn, run:
yarn add react@17.0.0 react-dom@17.0.0
Internal improvements
New JSX Transform
One of the greatest internal improvements React 17 has is the new JSX transform. Prior to React 17, when you write JSX code, it was transformed to a React.createElement
call.
So, it looked something like this:
import React from 'react'
const App = () => {
return <h1>Hello World</h1>
}
export default App
Was transformed to this:
import React from 'react'
const App = () => {
return React.createElement('h1', null, 'Hello World')
}
export default App
Starting on React 17, you don't need to import React from 'react'
anymore. Instead, two new entry points were added to the React package that are intended to be used only by compilers like Babel or TypeScript. So for the above JSX code, the output will be slightly different:
import { jsx as _jsx } from 'react/jsx-runtime'
const App = () => {
return _jsx('h1', { children: 'Hello world' })
}
export default App
So, your app won't need to import React from 'react'
anymore. Instead, you can directly write your JSX code:
const App = () => {
return <h1>Hello World</h1>
}
export default App
Next.js was known to allow the above because under the hood, it imported React. However, they changed this in their 9.5.3
release in order to use the new transform.
Updating ESlint rules
If you are using the eslint-plugin-react
package, you may notice not importing React will generate an issue. For that, you can just disable those rules while they remove those rules.
eslintrc.json
{
// ...
"rules": {
// ...
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off"
}
}
Removing Unused React Imports
Luckily, the React team has created a codemod you can run to remove your old React imports. To run it, just open a new terminal on your project directory, and run the following script:
npx react-codemod update-react-imports
A new Event Delegation System
To enable gradual updates, the React team changed its internal Event Delegation System, which works like this:
Before React 17, all event handlers were attached at the document
level under the hood, so it ran document.addEventListener()
for most events.
After React 17, it attached all event handlers to the root element where you render your app, which calls rootNode.addEventListener()
instead.
To better understand how the new Event Delegation System works, take a look at this diagram:
You can take a look at a demo of gradual updates on an example repository put together by the React team.
Other Breaking Changes
There are other minor breaking changes that hopefully won't affect your application. They are mostly internal changes, like the onScroll
event no longer bubbling, onFocus
and onBlur
events switching to use the native focusin
and focusout
under the hood, removing the old event pooling optimization, and making the effect cleanup timing more consistent.
To go deeper into these other breaking changes, you can take a look at the React v17 RC Breaking Changes.