React 19 has been released with a series of new and exciting changes that we will cover in this post. These changes include new features, improvements, deprecations, or replacements of current features.
Changes that enable us as software engineers to achieve more and better output with less code.
We will discuss all the major changes mentioned below:
React Compiler
forwardRef => Ref as a prop
Document Metadata
Server Components
Actions
New Hooks (use(), useFormStatus(), useOptimistic()
React Compiler
One of the longstanding issues in React has been unnecessary re-renders, which can harm the performance of a project. It wasn't logical to re-render a component simply because its parent component had re-rendered when the props it received hadn't changed. Imagine having many nested components that didn't need to re-render because their props hadn't changed, but they did anyway because the parent component re-rendered, slowing down the website's speed.
To solve this problem, the React team introduced techniques to allow us to memoize the data passed as props to prevent unnecessary re-renders.
These techniques involved the use of useMemo, useCallback, and Memo.
However, the issue with this approach is that many people don't know exactly when it's appropriate to use these techniques. Sometimes the re-render might not be worth it and could even worsen performance since these hooks involve a process of comparing previous props with new ones. (You can read more about this in an article I wrote about improving the performance of React projects.)
Secondly, in my opinion, they don't provide a particularly interesting Developer Experience.
After much feedback from the community and considerable time spent, the React team finally announced that they have been working on the React compiler and are currently using it on Instagram. They plan to release it in upcoming versions soon.
This compiler will transform your React code into pure JavaScript and handle the performance improvement and memoization process behind the scenes, so you won't need to use those hooks any more.
As a result, useCallback, useMemo, and memo will easily become redundant in React, simplifying our work.
ForwardRef => Ref as a prop
Previously, when we had a variable that was equal to useRef, and we wanted to pass this variable to a child component for use there, we would use forwardRef to be able to receive and use that passed ref in addition to the props, like this:
But now, there’s no need for that, and we can directly pass the ref as a prop, like this:
Thus, there is no need for forwardRef anymore.
Support for MetaData
In previous versions of React, whenever we wanted to add tags to the head of a document within a JSX component, we were forced to use libraries like React Helmet. But now, we can directly place meta tags, link tags, and others that belong in the head within JSX. React will automatically detect them, extract them, and place them in the head tag, like this:
Server Components
The topic of server components and client components has been around for a while, and we use it extensively in Next.js.
In general, server-side components are those that are built on the server side during the build process. By not being included in the JavaScript bundle, they reduce its size and improve performance. They also have several other advantages:
SEO Improvement: Server-side components are rendered on the server and returned as ready-made HTML, giving search engine crawlers access to more content, which improves SEO. In client-side code, since the components are built on the client and dynamically update the page content with JavaScript, initially, there is no content, and the page source is empty, so crawlers find nothing.
Performance Improvement: As mentioned in the first point, since server-side components are built or pre-built on the server during the build process, they load very quickly, resulting in better performance.
Security: Server components allow sensitive data like tokens and API keys to remain on the server side and not be accessible on the client side, which increases the site's security.
These are the major advantages, and if we only consider these, React was missing out on significant benefits of server-side rendering in previous versions. However, in the recent version, React has added server components. Unlike Next.js, React components are still client-side by default, and if we want them to be server-side rendered, we must add a directive like this to the first line of our component:
'use server';
import React from 'react';
...
This directive tells React to render the component on the server side, leveraging the benefits of server-side rendering.
Actions
Actions are a new feature in React that changes the way we handle forms. Previously, we relied on the submit button in the form and also needed states to access the values of the inputs within the form to submit the data. Now, with the use of the action attribute on the form tag, we can capture and submit the data in this manner:
As you can see, the action is set to a function that returns the data in the form, as the first parameter. We can use the get method it provides to retrieve the name of each input within the form and get the submitted value.
In the next section, I’ll introduce new hooks that have been added, and we’ll see even more improvements in handling forms.
UseFormState and useFormStatus
These two new hooks have been added to React to make working with forms easier for us.
useFormState():
This hook allows us to have a state and set the value of that state based on the result returned in the form action handler. Let’s see an example:
The code above is a scenario where the action and the useFormState hook have not been used, and we see that three inputs are defined for the inputs and the message that returns from our request.
Now, let’s write the same using the action and the useFormState hook:
In the code above, states have been removed because the parameter that the form action handler returns already contains the data for name and email, and the useFormStatus hook is used for the message.
This hook returns an array similar to useState. The first value of the array is the value returned by its action, and the second value is the action function itself, which we place in the action attribute of our form. The values that the hook takes are firstly a function that we want to call during our form action, which returns something that goes into the same message, and the second value it takes is the initial value that we want the message to have.
useFormStatus():
Imagine we want to add error handling and loading states to our form in the above example. Let’s first look at how we would write the code using the previous approach:
Now, let’s see how we can use the action and these two new hooks to handle the form with error and loading states:
In the code, we see that the submit button has been moved into a separate component, where we use the useFormStatus hook. This hook returns an object that includes properties like data and pending, along with some other properties that aren't very useful.
The data property is the data that has been obtained and submitted from the form, and the pending property is a boolean that remains true as long as our form action hasn't returned a result and is pending. Once the promise of our action is either fulfilled or rejected, the pending property becomes false.
Note: One of the pitfalls of this hook is that you cannot use it within the same component where the form exists; this hook only gives you the status of the form from the parent component.
What other new hooks do we have?
use()
This new hook that has been introduced to React is multi-purpose and is designed to simplify the process of fetching data from external sources as well as handling context:
1. Fetching Data from External Sources
The way we used to fetch data:
Previously, we needed a state and also a useEffect to fetch the data and set it in the state.
Now, with the use hook, there’s no need for a state and useEffect:
Now, something even cooler that we can do to enhance the user experience is to add a loading state while the data is being fetched. Surely you know that in the previous routine, we had to add a loading state, but Now, using React’s Suspense, the process has been made easier for us:
By wrapping our component with the Suspense component and adding a loading indicator as a fallback, the component will display the loading indicator until the data is fetched. This showcases the improved asset loading in the new version of React and signifies the end of React.lazy. This is not only due to this case but also because of the addition of server components and server-side pre-rendering, etc., which somewhat eliminates the need for React.lazy.
2. The second use of the use hook is for retrieving data from context
Previously, we used the useContext hook in components to read the data available in our context:
Now we can use the use hook:
This hasn't made a significant difference, but overall, if you're using use within a component, you can also use it to read from the context, so there's no need to import another hook.
Note: In the new version of React, instead of , you can simply use , and there's no need to include the word 'Provider'.
UseOptimistic()
This hook is useful in scenarios where you need to interact with the server, such as in real-time applications. For instance, imagine when you like a post, you’re essentially sending data to the server to register that like, or when you send a message to someone, you’re sending data to the server to record the message and display it in the chat box. Now, consider that it might take a few seconds for the like or message to reach the server and be successful. During this time, the user would see that nothing happens when they like or send a message.
the post isn’t liked, nor is the message sent...
This is where we use the useOptimistic hook to perform an optimistic update. By clicking, we assume that the data has been successfully recorded on the server, so we preemptively insert fake data as if it has come from the server and was successful, and the user immediately sees that the post has been liked. When the real data comes from the server, it replaces our fake data, and the user doesn’t notice because as soon as they like it, they see the post has been liked, and with the arrival of the real data, the data doesn’t change. Or, for example, when a message is sent, we consider that message as registered and immediately show it in the chat box so the user thinks their message has been recorded. This method is called an optimistic update, as evident from the name of the hook.
This improves the user experience by making the application feel more responsive and interactive. Here’s an example:
In the example above, we see that we have a messages state which holds the real messages, and we have the useOptimistic hook that takes two parameters. The first is the initial value, which is equal to the real messages, and it takes a callback that optimistically returns the previous messages plus our own fake message. This hook returns an array containing two values: the first is the data that is returned from the callback, and the second is the callback itself, which we can invoke wherever we want to optimistically update the data.
In the JSX, instead of mapping over the original messages, we map over the optimisticMessages because this includes the previous messages as well as our fake message, while the messages state remains untouched. Now, we have a form that contains the message input, and when the user writes their message and submits it, inside the sendMessage function, we see that it first calls our optimistic function and passes the message to it to update the optimisicMessages and consequently quickly update the UI. Then, it sends the message to the server to update the original data, and when the data returns, it updates the messages which contain the original data, causing the optimisticMessages to be refreshed with the real data, and the fake one is replaced with the real message.
These were all the significant changes we witnessed in React 19.
I hope you find them useful and enjoyable.
Top comments (0)