The world of front-end development is constantly evolving, with new frameworks and libraries continuously emerging to expand the possibilities of web development. Amidst this dynamic landscape, React has established itself as a pivotal tool for modern web development, renowned for its user-friendly nature, adaptability, and high performance. The highly anticipated release of React 19 is a big step in the library’s progression. This article will discuss the new features in React 19, and how they improve the developer experience and aid in making web applications more dynamic, efficient, and user-friendly.
The main contributor – React Compiler
The React Team first mentioned React Compiler, initially called React Forget, back in 2021. Since then, the developer community seemed to have forgotten about React Forget (no pun intended). However, it has not been overlooked by the React team, and it is set to be a part of the new release.
What problems does React Compiler solve?
The first and foremost highlight of the React Compiler is its automatic memoization feature. In simple terms, this prevents unnecessary re-renders, addressing a problem that React had previously tackled through manual “memoization“ strategies using well-known hooks such as useMemo
and useCallback
, as well as the memo
API.
To illustrate this, let’s examine a basic React Component:
<FilterTab/>
is a component that renders two additional components and serves as a parent of both. This code looks pretty, doesn’t it? However, if we ship it as it is, we might face such problems as unnecessary re-renders. What if only the heading changes? Due to the way React works, the entire component will re-render including its children. In this particular case, filteredList
will be created anew and passed down to <FilteredList/>
causing it to re-render.
Manual memoization to the rescue! In this case, we can wrap filteredList
in useMemo
hook and make it dependable only on list and filterCondition
:
This code does not look too shabby, either. Unfortunately, using the useMemo
hook does only half of the job. Now, if the heading changes, we do not recompute the filteredList
. Still, <FilterTab>
will cause <FilteredList/>
to re-render even if props passed down to it have not changed. If parent re-renders, child re-renders. To opt out of that, we, as developers who diligently memoize our code, should wrap <FilteredList/>
in React.memo()
API like so:
In this small example, it seems like it did not take a lot of time to use manual memoization and optimize the performance of the code. Yet, using a rich imagination, you can envision a real-life application with tons of components, which need to be considered and memoized. Things can get messy quickly, readability is a bit lost and, frankly, this is when you start losing a lot of the value of React.
Apart from that, it is not an easy and intuitive job to memoize things properly in React. Before using memoization hooks, one should consider composition techniques such as “moving state down“ or “passing components as children“.
Now, picture this: we can effortlessly write straightforward code similar to our very first example, while completely disregarding the memoization and the previously discussed design patterns. This is precisely the capability that the React Compiler will enable developers to do. No more unreadable and untrackable chains of props inside useMemo
and useCallback
. No more unnoticed children, which have been forgotten to be memoized. Writing code becomes easier and more intuitive. The code itself will remain very simple and procedural, and the output is going to be performant and optimized.
Additional features
React Compiler is by far the most exciting part of the upcoming release, but it is not the only feature we will enjoy when it ships. Some of the features, that were only available in React Canary, will get included in the stable version. Let’s explore these thrilling new features.
use() Hook
use() hook
can be used to get the value of a Promise or a context. In the case of context, it can replace useContext
hook. Yes, we will be able to write 6 letters less than before! The more exciting part is, however, resolving a promise like, for example, when fetching data from an API. It does not have to be packed in useEffect
when performed on a client side. The following code example shows how fetching data can be performed utilizing the use()
hook.
Let’s first have a look at how we would write it with a good old useEffect
hook. Component fetches some posts on initial render, shows “Loading …“ while the data is being fetched and, after getting response from an API, renders the data:
And this is how it will look after applying the use()
hook. In this case, there is no need to await a response, since use()
returns the resolved value of a Promise. Additionally, it enables using Suspense and error boundaries to, for example, show fallback as in this example:
As you can see, the code is much leaner and more readable in this case. With use()
hook, developers do not have to waste time tracking different states and props. An excellent example of Write Less, Do More
useOptimistic() Hook
useOptimistic() Hook
allows us to show temporary UI while waiting for an asynchronous task to resolve in the background. This is especially useful when you want to show an “optimistic“ result to a user while sending form data.
Directives
'use server'
and 'use client'
Directives will be now available for those who would like to explore the powers of Server Components, which, by the way, will be directly integrated into React with the upcoming release. It will offer numerous benefits such as:
- SEO will be improved with server-rendered components, as they provide more accessible content to web crawlers
- There will be a performance boost, especially for content-heavy applications, with faster initial page loads and overall improved performance.
- Server components will allow for code execution on the server, making tasks such as API calls seamless and efficient.
Actions
Another thrilling feature that will streamline and simplify form handling is actions. Actions will essentially replace the onSubmit
event handler with action
attribute. The main benefit of an action attribute is that you will have direct access to form data in your action, which reduces the boilerplate code.
useFormState() and useFormStatus()
Related to the action attribute and most likely to be used together with it, are the hooks called useFormState()
and useFormStatus()
. The former allows you to update the state according to the outcome of a form action. The latter provides details regarding the form submission status, such as ‚pending‘ or ‚method‘.
Asset loading
Normally, in React, the loading of assets has to be managed manually, especially when it comes to images and other files. Developers would have to add custom code to detect which assets are ready to be loaded to avoid inconsistent user experiences, such as flickering from a non-styled to a styled view. In React 19, the loading lifecycle of assets will be included in Suspense so that React will intelligently determine whether the content is ready to be displayed. Additionally, new Resource Loading APIs will be available to offer more control over loading and initializing resources.
Document metadata
Built-in support for meta tags such as <title>
, <meta>
, and <link>
is one more advantage that will be accessible with React 19. These elements are important for accessibility and SEO optimization. They will be supported in a fully client-side environment as well as in SSR and RSC. This improvement eliminates the requirement for using libraries such as React Helmet.
Web components
Integrating web components into React is not a simple process. Generally, you have to either convert the web component to a React component or install additional packages and write extra code to ensure compatibility with React. This can be quite challenging.
Fortunately, React 19 is going to simplify the process of integrating web components. At this point, it is not clear how exactly the integration of web components will look like. Hopefully, it will make the development process more streamlined and will allow us to utilize the extensive range of pre-existing web components within your React applications.
ref as a regular prop
How many of us have wished for ref
to be a simple prop? Well, now that wish has been granted, as ref
will become a regular prop that can be passed down through a component tree. This simplifies the process and removes the complexity of implementing forwardRef
wrappers.
Summary
React has an ambitious vision. React 19, in conjunction with the groundbreaking React Compiler, will transform our approach to web development. By focusing on streamlining code, improving performance, and introducing flexible features, the new release sets the stage for a smoother and more enjoyable development process.
Resources and Links
React Labs: What We’ve Been Working On – February 2024