Discover the Latest Advancements in React 19

April 4, 2024Author: Fabio J Raminhuk
new-features-react-19.jpg

Addressing a long-standing challenge of React, React 19 aims to tackle the issue of excessive re-rendering. This problem has historically consumed developers' time, resulting in performance concerns. Engineers have consistently faced the challenge of identifying the code responsible for triggering re-rendering and then optimizing it. However, React 19 will address this issue, providing a solution. The framework will now take care of re-rendering automatically, simplifying the development process.

 

In the past, developers utilized various methods such as useMemo(), useCallback(), memo, and similar techniques to handle re-rendering. However, React 19 eliminates the need for these manual interventions. By intelligently recognizing and memoizing code behind the scenes, the framework enhances the code's cleanliness and efficiency.

 

New Features React 19

React compiler

The React team is currently developing a new compiler that will be integrated into future versions of React. This cutting-edge technology is already being utilized by Instagram and will soon be available to the wider React community.

Server components

After an extensive period of development, React has unveiled server components, a groundbreaking addition to its framework. This exciting new feature is now available for implementation in Next.js, providing users with enhanced capabilities.

Actions

There is a forthcoming revolution in the way we engage with DOM elements through the implementation of actions.

Document Metadata

Additionally, developers will soon have the ability to achieve greater results with less code, thanks to the anticipated enhancement of document metadata.

Assets Loading

The loading of assets is undergoing enhancements that will have a twofold effect: improving the application's load time and enhancing the overall user experience.

Web components

An intriguing aspect of this is the integration of web components into React code, which opens up a multitude of exciting possibilities. I am filled with anticipation for the potential that this development holds.

Enhanced hooks

Additionally, there are exciting new hooks on the horizon that have the potential to revolutionize our coding experience.

 

Introducing the React Compiler: Streamlining React Rendering

React Compiler marks a pivotal shift in React's rendering mechanism. Historically, React lacked automatic re-rendering on state changes, necessitating manual optimization through useMemo(), useCallback(), and memo APIs. While React's team deemed this a "reasonable manual compromise," community feedback spurred a reevaluation.

 

Recognizing the cumbersome nature of manual optimization, the React team embarked on a solution: the React Compiler. This revolutionary tool empowers React to autonomously manage re-renders, dynamically adjusting state changes to update the UI seamlessly.

 

Gone are the days of manual intervention for developers. The React Compiler eliminates the need for useMemo(), useCallback(), and memo, streamlining the development process. Though slated for release in an upcoming React version, you can explore the compiler further through available resources.

 

With React Compiler, developers relinquish control to React, allowing it to optimize components and re-renders as needed, ensuring efficient performance.

 

Unlocking the Power of Server Components in React and Next.js

Server components represent a game-changing advancement in React and Next.js, yet many developers are unaware of their potential. Traditionally, React components operate on the client side. However, the introduction of server components revolutionizes this paradigm by enabling components to execute on the server side.

 

While the concept of server components has circulated for years, Next.js stands out as the trailblazer, implementing them for production. In Next.js 13 and beyond, all components default to server components. To designate a component for client-side execution, the "use client" directive is employed.

 

The integration of server components directly into React 19 heralds numerous benefits:

  1. SEO Enhancement: Server-rendered components bolster search engine optimization by offering more accessible content to web crawlers.
  1. Performance Enhancement: Server components contribute to faster initial page loads and enhanced overall performance, particularly for content-rich applications.
  1. Server-Side Execution: Server components facilitate server-side code execution, streamlining tasks such as API calls.
 

These advantages underscore the transformative potential of server components in contemporary web development. In React, all components default to the client side. Only with the inclusion of 'use server' does a component become a server component. Consider the following example:

 
'use server';

export default async function requestUsername(formData: FormData): Promise<string> {
  const username = formData.get('username');
  if (canRequest(username)) {
    // ...
    return 'successful';
  }
  return 'failed';
}

This code, written in React, executes on the server. By appending 'use server' as the initial line of the component, it becomes a server component, exclusively running server-side.

But how can developers leverage server components effectively?

 

By importing requestUsername into any React component within the same project, developers gain access to server components. Utilizing "Actions" (to be discussed shortly), developers can execute specific tasks. Currently, Next.js offers support for server-side components. For further insights into server components in Next.js, refer to the documentation. With React 19, server component support will be seamlessly integrated into React, empowering developers to harness their full potential.

 

Integrating Actions with Forms in React 19: Simplifying Data Handling

In the upcoming version 19 release, we're thrilled to introduce a groundbreaking feature called Actions, poised to revolutionize form workflows.

Actions empower you to seamlessly integrate actions directly within the <form/> HTML tag. Put simply, they offer a modern alternative to the traditional onSubmit event. With Actions, you can harness HTML form attributes to enhance form functionality like never before.

 

Before Actions: In the example snippet provided, we're employing the onSubmit React event to initiate the search method upon form submission. However, it's crucial to acknowledge the current limitation: the search method operates exclusively on the client-side. At present, we're constrained to employing React events for form submission, preventing server-side execution of the search functionality.

 
<form onSubmit={search}>
  <input name="query" />
  <button type="submit">Search</button>
</form>
 

After Actions: With the advent of server components, Actions usher in the ability to execute tasks on the server side. In our JSX, leveraging the <form/> element, we can eschew the traditional onSubmit event in favor of the action attribute. This attribute's value will be a method designated for submitting data, whether it's handled on the client or server side.

 

Actions afford the flexibility to perform both synchronous and asynchronous operations, streamlining data submission management and facilitating state updates. The overarching aim is to simplify form interaction and data handling processes.

Let's delve into an illustrative example:

"use server"

const submitData = async (userData) => {
    const newUser = {
        username: userData.get('username'),
        email: userData.get('email')
    }
    console.log(newUser)
}

const Form = () => {
    return <form action={submitData}>
        <div>
            <label>Name</label>
            <input type="text" name='username'/>
        </div>
        <div>
            <label>Email</label>
            <input type="text" name="email" />
        </div>
        <button type='submit'>Submit</button>
    </form>
}

export default Form;

In the provided code snippet, submitData serves as the action within the server component. The form, a client-side component, utilizes submitData as its action. This results in the execution of submitData on the server. The seamless communication between the client (form) and server (submitData) components is facilitated solely by the action attribute.

 

Utilizing the form and action mechanism, we can adeptly manage data submission both on the client and server sides.

 

Web Components

Web components empower you to craft customized components using native HTML, CSS, and JavaScript, seamlessly integrating them into your web applications as if they were standard HTML tags. Remarkable, isn't it?

 

Presently, incorporating web components into React poses certain challenges. Typically, you either have to convert the web component to a React component or install additional packages and write supplementary code to enable web components to function with React. This can prove to be quite frustrating. Fortunately, React 19 promises to simplify the integration of web components into your React projects. When encountering a particularly useful web component, such as a carousel, you'll be able to effortlessly integrate it into your React applications without the necessity of converting it into React code.

 

This streamlines the development process and allows you to harness the extensive ecosystem of pre-existing web components within your React projects.

However, specific details regarding the implementation are currently unavailable. Nonetheless, I remain optimistic that it will involve straightforwardly importing a web component into a React codebase, akin to module federation. I eagerly anticipate further insights on this implementation from the React team.

 
 

Improving Document Metadata Handling

 

The optimization of SEO and ensuring accessibility heavily relies on elements such as "title," "meta tags," and "description." Within the React ecosystem, particularly in the prevalent landscape of single-page applications, the task of efficiently handling these elements across diverse routes can be quite challenging.

 

Presently, developers often resort to crafting bespoke solutions or employing tools like react-helmet to navigate route transitions and dynamically update metadata. However, this process often leads to repetition and susceptibility to errors, especially when dealing with elements critical for SEO, like meta tags.

 

Before:

import React, { useEffect } from 'react';

const HeadDocument = ({ title }) => {
  useEffect(() => {
    document.title = title;

 	const metaDescriptionTag = document.querySelector('meta[name="description"]');
    if (metaDescriptionTag) {
    metaDescriptionTag.setAttribute('content', 'New description');
    }
  }, [title]);

  return null;
};

export default HeadDocument;

In the provided code snippet, we encounter the HeadDocument component, tasked with dynamically updating titles and meta tags based on provided props. This functionality resides within the useEffect hook, utilizing JavaScript to manipulate these elements. Nonetheless, this approach falls short in terms of elegance and adaptability, particularly concerning route management.

 

After:

With the introduction of React 19, we gain the ability to directly incorporate title and meta tags within our React components:

const HomePage = () => {
  return (
    <>
      <title>Freecodecamp</title>
      <meta name="description" content="Freecodecamp blogs" />
      {/* Page content */}
    </>
  );
}

This advancement allows for seamless integration of metadata within React components, facilitating enhanced control and optimization of SEO elements directly within the component structure.

 

Optimizing Asset Loading

Within React development, meticulous management of asset loading is essential to ensure smooth application performance, especially concerning images and other resource files.

Traditionally, the browser renders the view first, followed by stylesheets, fonts, and images, leading to a potential flicker from unstyled to styled content.

 

To address this issue, developers often implement custom solutions to detect asset readiness, delaying the display until everything is fully loaded.

In React 19, significant improvements are introduced where images and files load in the background as users navigate the page, enhancing page load times and reducing wait times.

 

Furthermore, React introduces the Suspense lifecycle for asset loading, encompassing scripts, stylesheets, and fonts. This lifecycle feature empowers React to determine when content is ready for display, eliminating any unstyled flickering.

New Resource Loading APIs such as preload and preinit offer enhanced control over when a resource should load and initialize, further optimizing performance.

By enabling asynchronous loading of assets in the background, React 19 significantly reduces wait times, ensuring uninterrupted user interaction with the content. These optimizations not only boost React application performance but also contribute to a more seamless browsing experience for users.

 

Exciting Updates: New React Hooks

React Hooks have revolutionized the way developers work with React, quickly becoming one of the most cherished features of the library. If you've been using React, chances are you've leveraged its built-in hooks extensively, and you might have even experimented with crafting your own custom hooks. Hooks have transcended mere functionality; they've evolved into a fundamental programming pattern within the React ecosystem.

With the upcoming release of React 19, expect significant changes in how useMemo, forwardRef, useEffect, and useContext are utilized. These changes are primarily driven by the introduction of a new hook: use.

 

useMemo():

Gone are the days of explicitly invoking the useMemo() hook. React 19 brings about an automatic memoization process handled by the React Compiler itself.

 

Before:

import React, { useState, useMemo } from 'react';

function ExampleComponent() {
  const [inputValue, setInputValue] = useState('');

  // Memoize the result of checking if the input value is empty
  const isInputEmpty = useMemo(() => {
    console.log('Checking if input is empty...');
    return inputValue.trim() === '';
  }, [inputValue]);

  return (
    <div>
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
        placeholder="Type something..."
      />
      <p>{isInputEmpty ? 'Input is empty' : 'Input is not empty'}</p>
    </div>
  );
}

export default ExampleComponent;
 

After:


import React, { useState } from 'react';

function ExampleComponent() {
  const [inputValue, setInputValue] = useState('');

  // Memoization handled internally by React 19
  const isInputEmpty = () => {
    console.log('Checking if input is empty...');
    return inputValue.trim() === '';
  });

  return (
    <div>
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
        placeholder="Type something..."
      />
      <p>{isInputEmpty ? 'Input is empty' : 'Input is not empty'}</p>
    </div>
  );
}

export default ExampleComponent;

In the example above, notice how React 19 simplifies the code by eliminating the need for explicit memoization, resulting in cleaner and more concise code. Stay tuned for more exciting updates as React continues to evolve!

 

forwardRef()

In React 19, there's a notable change in how refs are managed. Instead of using the forwardRef() hook, refs will now be passed as props directly, streamlining the code and simplifying its structure.

 

Before:

import React, { forwardRef } from 'react';

const ExampleButton = forwardRef((props, ref) => (
  <button ref={ref}>
    {props.children}
  </button>
));
 

After:


import React from 'react';

const ExampleButton = ({ ref, children }) => (
  <button ref={ref}>
    {children}
  </button>
);

With this adjustment, there's no longer a need for the forwardRef() hook. Refs are seamlessly incorporated as props, enhancing the clarity and readability of your React components. Keep embracing these enhancements as React evolves to simplify your development experience!

 

Introducing the Revolutionary use() Hook in React 19

React 19 brings forth a groundbreaking addition to its arsenal of hooks: the versatile use() hook. This new hook promises to simplify the management of promises, asynchronous code, and context within your React applications.

 

Let's delve into the syntax:

const value = use(resource);

Here's a practical example demonstrating the use() hook to perform a fetch request:

import { use } from "react";

const fetchUsers = async () => {
    const res = await fetch('https://jsonplaceholder.typicode.com/users');
    return res.json();
};
  
const UsersItems = () => {
    const users = use(fetchUsers());
  
    return (
        <ul>
            {users.map((user) => (
                <div key={user.id} className='bg-blue-50 shadow-md p-4 my-6 rounded-lg'>
                    <h2 className='text-xl font-bold'>{user.name}</h2>
                    <p>{user.email}</p>
                </div>
            ))}
        </ul>
    );
}; 
export default UsersItems;
 

Key points to understand:

  • fetchUsers initiates a GET request.
  • The use() hook is utilized to execute fetchUsers, eliminating the need for useEffect or useState hooks.
  • The return value of the use() hook, users, contains the response of the GET request.
  • Within the return block, users is mapped over to create the list.
 

Furthermore, the use() hook can also enhance your usage of Context. The Context API, renowned for managing global states in React without external state management libraries, now benefits from the use hook, as illustrated in the code snippet below:

import { createContext, useState, use } from 'react';

const ThemeContext = createContext();

const ThemeProvider = ({ children }) => {
    const [theme, setTheme] = useState('light');

    const toggleTheme = () => {
        setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
    };

    return (
        <ThemeContext.Provider value={{ theme, toggleTheme }}>
            {children}
        </ThemeContext.Provider>
    );
};

const Card = () => {
    // use Hook()
    const { theme, toggleTheme } = use(ThemeContext);

    return (
        <div
            className={`p-4 rounded-md ${
                theme === 'light' ? 'bg-white' : 'bg-gray-800'
            }`}
        >
            <h1
                className={`my-4 text-xl ${
                    theme === 'light' ? 'text-gray-800' : 'text-white'
                }`}
            >
                Theme Card
            </h1>
            <p className={theme === 'light' ? 'text-gray-800' : 'text-white'}>
                Hello!! use() hook
            </p>
            <button
                onClick={toggleTheme}
                className='bg-blue-500 hover:bg-blue-600 text-white rounded-md mt-4 p-4'
            >
                {theme === 'light' ? 'Switch to Dark Mode' : 'Switch to Light Mode'}
            </button>
        </div>
    );
};

const Theme = () => {
    return (
        <ThemeProvider>
            <Card />
        </ThemeProvider>
    );
};

export default Theme;
 

Key points to note:

  • ThemeProvider facilitates context provision.
  • Card is the component responsible for consuming the context, utilizing the new use hook for context consumption.
  • React 19 introduces additional hooks for handling form status and data, promising smoother form interactions. When combined with actions, another new feature of React 19, these hooks streamline form handling and data management.

Embrace the power of the use() hook in React 19 to unlock enhanced efficiency and simplicity in your application development workflows.

 

Harnessing the Potency of useFormStatus() Hook in React 19

In React 19, the advent of the useFormStatus() hook empowers developers with heightened control over form interactions, furnishing invaluable insights into the status of the last form submission. Let's explore its syntax and functionality:

 

Syntax:

const { pending, data, method, action } = useFormStatus();

Alternatively, a simpler version can be employed:

const { status } = useFormStatus();
 

Here's a breakdown of the key attributes:

  • pending: Indicates whether the form is currently in a pending state. It evaluates to true if pending, otherwise false.
  • data: An object conforming to the FormData interface, encapsulating the data submitted by the parent <form>.
  • method: Specifies the HTTP method utilized, defaulted to GET.
  • action: A reference to a function.

This hook serves to facilitate the display of a pending state and the data being submitted by the user.

 

Let's delve into a practical code example:

import { useFormStatus } from "react-dom";

function Submit() {
    const status = useFormStatus();
    return <button disabled={status.pending}>{status.pending ? 'Submitting...' : 'Submit'}</button>;
}

const formAction = async () => {
    // Simulate a delay of 3 seconds
    await new Promise((resolve) => setTimeout(resolve, 3000));
}

const FormStatus = () => {
    return (
        <form action={formAction}>
            <Submit />
        </form>
    );
};

export default FormStatus;
 

Key insights into the code:

  • Submit: This method serves as the action for form submission. It leverages useFormStatus to ascertain whether status.pending is true or false.
  • Based on the status.pending value, the appropriate message is displayed in the UI.
  • formAction: A simulated method introducing a delay of 3 seconds to mimic form submission.

In essence, upon form submission, the useFormStatus hook furnishes crucial insights into the form's pending status. While pending, the UI displays the message "Submitting..."; once the pending status resolves, the message transitions to "Submit".

 

Equipped with this robust hook, developers can seamlessly monitor form submission statuses and dynamically adjust UI elements accordingly, facilitating a refined user experience.

 

Exploring the Enhanced useFormState() Hook in React 19

In React 19, the introduction of the useFormState() hook revolutionizes state management within form submissions, enabling seamless updates based on submission outcomes. Let's delve into its syntax and functionality:

 

Syntax:

const [state, formAction] = useFormState(fn, initialState, permalink?);
  • fn: The function invoked upon form submission or button press.
  • initialState: The initial state value, which can be any serializable value. Once the action is initially invoked, this argument becomes irrelevant.
  • permalink: Optional. Specifies a URL or page link. If fn is executed on the server, the page redirects to permalink.

Upon invocation, this hook returns:

  • state: The initial state value specified by initialState.
  • formAction: An action passed to the form, with its return value reflected in the state.
 

Let's explore a practical implementation:

import { useFormState } from 'react-dom';

const FormState = () => {
    const submitForm = (prevState, queryData) => {
        const name =  queryData.get("username");
        console.log(prevState); // previous form state
        if(name === 'john'){
            return {
                success: true,
                text: "Welcome"
            }
        }
        else{
            return {
                success: false,
                text: "Error"
            }
        }
    }

    const [ message, formAction ] = useFormState(submitForm, null);

    return (
        <form action={formAction}>
            <label>Name</label>
            <input type="text" name="username" />
            <button>Submit</button>
            {message && <h1>{message.text}</h1>}
        </form>
    );
}

export default FormState;

Key insights into the code:

  • submitForm: This method governs form submission. It evaluates the form's content and returns a specific value and message based on success or error.
  • The initial state, null, is passed to useFormState. Upon invocation, it returns the previous form state.
  • Based on the name entered into the form, the message displayed dynamically updates to reflect either a "welcome" or "error" message.
 

Through this example, users encountering the name "John" receive a "welcome" message, while others encounter an "error" message, showcasing the dynamic nature of useFormState() in managing form submission outcomes.

Embrace the power of useFormState() in React 19 to streamline form interactions and enhance user experiences.

 

Introducing the Dynamic useOptimistic() Hook in React

In React, the useOptimistic() hook stands as a powerful tool to provide users with immediate feedback while asynchronous actions are in progress. This enhances the user experience by presenting an optimistic view of successful outcomes, even before the actions are completed. Let's explore its syntax and functionality:

 

Syntax:

const [ optimisticState, updateState ] = useOptimistic(state, updateFn);

This hook operates on the premise of showing a different state during asynchronous actions, offering a prompt response to users. Once the actual response is received from the server, the optimistic state is replaced accordingly.

 

Let's delve into a practical implementation:

import { useOptimistic, useState } from "react";

const Optimistic = () => {
  const [messages, setMessages] = useState([
    { text: "Hey, I am initial!", sending: false, key: 1 },
  ]);

  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (state, newMessage) => [
      ...state,
      {
        text: newMessage,
        sending: true,
      },
    ]
  );

  async function sendFormData(formData) {
    const sentMessage = await fakeDelayAction(formData.get("message"));
    setMessages((messages) => [...messages, { text: sentMessage }]);
  }

  async function fakeDelayAction(message) {
    await new Promise((res) => setTimeout(res, 1000));
    return message;
  }

  const submitData = async (userData) => {
    addOptimisticMessage(userData.get("username"));
    await sendFormData(userData);
  };

  return (
    <>
      {optimisticMessages.map((message, index) => (
        <div key={index}>
          {message.text}
          {!!message.sending && <small> (Sending...)</small>}
        </div>
      ))}
      <form action={submitData}>
        <h1>OptimisticState Hook</h1>
        <div>
          <label>Username</label>
          <input type="text" name="username" />
        </div>
        <button type="submit">Submit</button>
      </form>
    </>
  );
};

export default Optimistic;

Key insights into the code:

  • The useOptimistic() hook manages the optimistic state, allowing the immediate display of pending messages.
  • The addOptimisticMessage function is used to append new messages to the optimistic state, providing real-time feedback to users.
  • Upon form submission, the submitData function triggers the addition of an optimistic message and initiates the asynchronous form submission process.
  • The fakeDelayAction function simulates a delay in the form submission process to showcase the optimistic state.
 

Through this implementation, users are provided with immediate feedback upon form submission, enhancing the overall user experience. The useOptimistic() hook empowers developers to optimize user interactions and streamline asynchronous actions effectively.

notion image
 

Is React 19 Ready for Use?

Currently, all the functionalities highlighted earlier are accessible in the canary release. Further details can be found here. The React team advises against utilizing these features for customer-facing applications at this time. However, feel free to experiment for personal learning or entertainment purposes.

If you're curious about the release date of React 19, you can monitor the Canary Releases for any updates.

For additional information, stay updated by following the React team on the following platforms:

Official Website GitHub Canary Releases

 

References

New Features in React 19 – Updates with Code Examples

React Labs: What We've Been Working On – February 2024

Unveiling the Future: React 19's Revolutionary Features | React 19 Update

 
Tags:
React-19ReactJavaScriptHooksTypeScriptCompoentsWeb Development