Finally , React 18 was released in March 2022. Officially, React 18 is now ready to use by globe .
This release comes up with minimal Breaking changes ,New Hooks & APIs . It Also includes some out-of-box performance and architectural improvements (SSR) and updating the re-rendering engine ( like automatic batching, startTransition API , etc.)
Many of the features in react 18 sets the foundation of the opt-in “Concurrent rendering” key property which unlocks new capabilities.
So let’s dive into some exciting new improvements to React 18 features……
React 18 Feature Quick Guide:
Sr.no | Category | Features |
1 | Breaking Changes / Deprecated | ReactDOM.render |
2 | Concept | Concurrent React Re-rendering |
3 | Features | Automatic Batching, Transitions, Suspense on the server |
4 | Hooks | useId, useTransition, useDeferredValue, useSyncExternalStore, useInsertionEffect |
5 | APIs | createRoot, hydrateRoot, renderToPipeableStream, renderToReadableStream |
6 | Updates | Strict mode |
How to upgrade to React 18 ?
Install React 18 and React DOM from npm like this:
npm install react react-dom
Or if you’re using yarn:
yarn add react react-dom
Let’s discuss features and updates in detail …..
Breaking Changes / Deprecated :
ReactDOM.render:– ReactDom.render is no longer supported in react 18 . Use createRootAPI otherwise it will behave like React 17.
Improvements to Root API:
In React, a “root” is a pointer to the top-level data structure that React uses to track a tree to render. React 18 ships to ReactDOM.createRoot ( New Root API ) then passes the root to the render function. Before React 18 we were using ReactDOM.render method (Legacy root API ReactDOM.render) .
- Legacy root API: This is the existing API called with ReactDOM.render. This creates a root running in “legacy” mode, which works exactly the same as React 17.
- New root API: The new Root API is called with ReactDOM.createRoot. This creates a root running in React 18, which adds all of the improvements of React 18 and allows you to use concurrent features.
If we use ReactDOM.render it will work but shows the Deprecated
– ReactDom.Render() method deprecated
React 17:
// Before
import { render } from 'react-dom';
const container = document.getElementById('app');
render(<App tab="home" />, container);
React 18:
// After
import { createRoot } from 'react-dom/client';
const container = document.getElementById('app');
const root = createRoot(container); // createRoot(container!) if you use TypeScript
root.render(<App tab="home" />);
Reason to change the API:
1)Don’t need to store the root on the DOM node.
2)This change allows us to remove the hydrate method and remove the render callback.
Concept /Key Property:
Concurrent React Re-rendering :
Concurrency is not a feature, It’s a new behind-the-scenes mechanism that enables React to prepare multiple versions of your UI at the same time.
A key property of Concurrent React is that rendering is interruptible.
Lets have an example to understand this ….
If we want to have a call with John and Albert then we can call one after the other , this is a non-concurrent call . It is possible for short call
But if a call with John takes longer and holds for some time then we can have a call with Albert in that . it’s not that we are on call with both at the same time. Its concerting example.
Similarly, in React 18 with concurrent rendering, React can interrupt, pause, resume, or abandon a render. This allows React to respond to the user interaction quickly even if it is in the middle of a heavy rendering task.
For more details refer discussion link.
New React 18 Features:
1. Automatic Batching:
Automatic batching means batched multiple state updates do single render to avoid multiple re-rendering . Previously , React batched the state updates within event handlers which causes performance issues for large components. React 18 introduces automatic batching which allows all state updates – even within promises, set Timeouts, and event callbacks – to be batched. React will wait for a micro-task to finish before re-rendering.
// Before: only React events were batched.
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React will render twice, once for each state update (no batching)
}, 1000);
// After: updates inside of timeouts, promises,
// native event handlers or any other event are batched.
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React will only re-render once at the end (that's batching!)
}, 1000);
We can opt-out of (avoid) automatic batching by using flushSync.
2. Transitions:
A transition distinguishes between urgent and non-urgent updates.
- Urgent updates reflect direct interaction, like typing, clicking, pressing, and so on.
- Transition updates transition the UI from one view to another.
Consider , we have an input text field and search button . It’s urgent to show typed text in the field, not search results to the user.
These non-urgent updates are called transitions. By marking non-urgent UI updates as “transitions”, React will know which updates to prioritize. This makes it easier to optimize rendering.
we can use startTransition API inside an input event to inform React which updates are urgent and which are “transitions”:
import {startTransition} from 'react';
// Urgent: Show what was typed
setInputValue(input);
// Mark any state updates inside as transitions
startTransition(() => {
// Transition: Show the results
setSearchQuery(input);
});
Updates wrapped in startTransition are handled as non-urgent and will be interrupted if more urgent updates like clicks or key presses come in.
- useTransition: a hook to start transitions, including a value to track the pending state.
- startTransition: a method to start transitions when the hook cannot be used.
3. Suspense on the server:
React 18 has added an architectural improvement to the react server-side rendering.
- Client side rendering:
In a client-rendered app, you load the HTML of your page from the server along with all the JavaScript that is needed to run the page, and make it interactive.
But for a heavy JS bundle file, the user needs to wait on a blank screen.
Fig . Client Rendering (reference link)
- Server side rendering:
Server-side rendering generates HTML from the react components on the server and sends it back to the client, so the client can now see the page content before the JavaScript bundle loads and runs.
Fig . Server Rendering (reference link)
Suspense will fall back to the component you give it if any of the components in the tree “suspend”.
<Suspense fallback={<Spinner />}>
<Comments />
</Suspense>
Suspense in React 18 works best when combined with the transition API. If you suspend during a transition, React will prevent already-visible content from being replaced by a fallback. Instead, React will delay the render until enough data has loaded to prevent a bad loading state.
For more detail about SSR link
Hooks:
There are 5 new Hooks introduce in React 18
- useId,
- useTransition,
- useDeferredValue,
- useSyncExternalStore,
- useInsertionEffect
Let’s see in detail…
- useId:- is a new hook for generating unique IDs on both the client and server, while avoiding hydration mismatches.
function CodeUseId() {
const id = useId();
return (
<>
<label htmlFor={id}>useId</label>
<input id={id} type="checkbox" name="coc" />
</>
);
useId is not for generating keys in a list. Keys should be generated from your data
2. useTransition:- useTransition and startTransition let you mark some state updates as not urgent.
function App() {
const [isPending, startTransition] = useTransition();
const [count, setCount] = useState(0);
function handleClick() {
startTransition(() => {
setCount(c => c + 1);
})
}
return (
<div>
{isPending && <Spinner />}
<button onClick={handleClick}>{count}</button>
</div>
);
}
3. useDeferredValue:- useDeferredValue lets you defer re-rendering a non-urgent part of the tree. It is similar to debouncing or throttling, but has a few advantages. There is no fixed time delay, so React will attempt the deferred render right after the first render is reflected on the screen. The deferred render is interruptible and doesn’t block user input.
function Typeahead() {
const query = useSearchQuery('');
const deferredQuery = useDeferredValue(query);
// Memoizing tells React to only re-render when deferredQuery changes,
// not when the query changes.
const suggestions = useMemo(() =>
<SearchSuggestions query={deferredQuery} />,
[deferredQuery]
);
return (
<>
<SearchInput query={query} />
<Suspense fallback="Loading results...">
{suggestions}
</Suspense>
</>
);
}
4. useSyncExternalStore :- useSyncExternalStore is a new hook that allows external stores to support concurrent reads by forcing updates to the store to be synchronous. It removes the need for useEffect when implementing subscriptions to external data sources.
5. useInsertionEffect :- The signature of useInsertionEffect is the same as useEffect, but it fires synchronously before all DOM mutations. This hook is meant to inject styles into the DOM before reading layout in useLayoutEffect.
Conclusion:
React 18 has out-of-box features , new Hooks and API which improves the performance and user experience.
Blog Written By: Priyanka Tawale
For more detail React 18 features refer:
https://reactjs.org/blog/2022/03/29/react-v18.html
https://github.com/reactjs/rfcs/blob/main/text/0213-suspense-in-react-18.md
https://github.com/reactwg/react-18/discussions/46