Hey Coders! If you are a react developer then you might have already heard about the latest version of React- React 18 Alpha. The team is still working on the update and there is still a lot to come, so in this article let's see what's happening in the version and breakdown it into simple.
The first thing that comes to our mind every time there is a version update is the latest set of changes will break anything with your current setup, or whether you will have to learn new completely unrelated concepts?
The answer is no, we will be able to adopt React 18 without rewrites and try the new features at our own pace.
React 18 – what can we expect?
- out-of-the-box improvements (including automatic batching),
- new streaming server renderer with built-in support for React.lazy,
- other concurrent features such as startTransition, useDeferredValue,
- new root API.
This release is more focused on User Experience and internal architecture changes, including adaptation to concurrent features. However, the most important, new addition in React 18 seems to be the concurrent rendering and the related concurrent mode.
1. Automatic batching React 18 adds out-of-the-box performance improvements by doing more batching by default, removing the need to manually batch updates in application or library code.
But, what is batching? Batching is when React groups multiple state updates into a single re-render for better performance. In simple words, batching (grouping) means multiple state updates are combined into a single render. Whenever you are using setState to change a variable inside any function, instead of making a render at each setState, React instead collects all setStates and then executes them together. This is known as batching.
function App() {
const [count, setCount] = useState(0);
const [flag, setFlag] = useState(false);
function handleClick() {
setCount(c => c + 1); // Does not re-render yet
setFlag(f => !f); // Does not re-render yet
// React will only re-render once at the end (that's batching!)
}
return (
<div>
<button onClick={handleClick}>Next</button>
<h1 style={{ color: flag ? "blue" : "black" }}>{count}</h1>
</div>
);
}
This is great for performance because it avoids unnecessary re-renders. However, React didn't use to be consistent about when it performed batching. This was because React used to only batch updates during browser events (like a click), but here we’re updating the state after the event has already been handled (in a fetch callback):
function App() {
const [count, setCount] = useState(0);
const [flag, setFlag] = useState(false);
function handleClick() {
fetchSomething().then(() => {
// React 17 and earlier does NOT batch these because
// they run *after* the event in a callback, not *during* it
setCount(c => c + 1); // Causes a re-render
setFlag(f => !f); // Causes a re-render
});
}
return (
<div>
<button onClick={handleClick}>Next</button>
<h1 style={{ color: flag ? "blue" : "black" }}>{count}</h1>
</div>
);
}
What if I don’t want to batch? Usually, batching is safe, but some codes may depend on reading something from the DOM immediately after a state change. For those use cases, you can use ReactDOM.flushSync() to opt-out of batching:
import { flushSync } from 'react-dom'; // Note: react-dom, not react
function handleClick() {
flushSync(() => {
setCounter(c => c + 1);
});
// React has updated the DOM by now
flushSync(() => {
setFlag(f => !f);
});
// React has updated the DOM by now
}
2. Server-Side Rendering Server-side rendering is a way of rendering the JS data to HTML on the server to save computation on the frontend. This results in a faster initial page load in most cases.
React performs Server Side Rendering in 4 sequential steps:
- On the server, data is fetched for each component.
- On the server, the entire app is rendered to HTML and sent to the client.
- On the client, the JavaScript code for the entire app is fetched.
- On the client, the JavaScript connects React to the server-generated HTML, which is known as Hydration. In the trivial version(till React 17), SSR had to load the entire page before it can start hydrating the page.
This changes in React18, now we can break React components into smaller chunks using .
Streaming HTML
<Suspense fallback={<Spinner />}>
{children}
</Suspense>
By wrapping the component in , we tell React that it doesn’t need to wait for comments to start streaming the HTML for the rest of the page. Instead, React will send the placeholder (a spinner) instead.
When the data for the comments is ready on the server, React will send additional HTML into the same stream, as well as a minimal inline script tag to put that HTML in the "right place".
Selective Hydration Before React 18, hydration couldn't start if the complete JavaScript code for the app hadn't loaded in. For larger apps, this process can take a while.
lets you hydrate the app before the child components have loaded in.
By wrapping components in , you can tell React that they shouldn’t block the rest of the page from streaming—and even hydration. This means that you no longer have to wait for all the code to load in order to start hydrating. React can hydrate parts as they’re being loaded.
3. startTransition One important use case for startTransition could be when a user starts typing in a search box. The input value has to be immediately updated while the search results could wait few milliseconds(as expected by the user).
This API provides a way to differentiate between quick updates and delayed updates. The delayed update(i.e. transition of one UI view to another) is termed as Transition Updates.
For urgent updates like typing, hover, clicking, we call props/functions usually like this :
setText(input)
For non-urgent or heavy UI updates, we can wrap it in a startTransition API as :
startTransition(() => {
setText(input);
});
4.The New Root API We usually create a Root level DOM like this and append the React App. This has now been deprecated and is now called "Legacy Root API"
import React from 'react';
import ReactDOM from 'react-dom';
const container = document.getElementById('root')
ReactDOM.render(<App />, container);
Instead, a new Root API is introduced in React18, which looks like this :
import React from 'react';
import ReactDOM from 'react-dom';
import App from 'App'
const container = document.getElementById('root')
const root = ReactDOM.createRoot(container)
root.render(<App />)
React18 will ship with both Legacy Root API and the New Root API to maintain a smooth transition of React 17(or older) apps to React 18.
Wrapping-Up So to summarize, the features that React 18 brings are:
- Concurrency control with the Transition API,
- Automatic Batching of function calls and events to improve in-app performance, and
- Much faster page loads for SSR with Suspense.
React 18 docs React 18 discussions
Thank you so much for reading this article! I hope this was useful to you in some way. Happy Coding💜