HomeBlogWorksAbout

React 19 Beta: A Deep Dive into the Latest Features

React, one of the most popular JavaScript libraries for building user interfaces, is taking a big step forward with the release of its 19th version. As of April 25, 2024, the beta version of React 19 is officially available, bringing exciting new features to the table. While the stable release date remains uncertain, this beta offers a glimpse into what’s next for React. Check out this blog post to dive deeper into the upcoming release. So, what’s new in React 19?

Let’s explore!

1. New Compiler: React Compiler

React 19 introduces a groundbreaking new compiler that understands your code ahead of time, transforming it into more efficient output and overcoming previous limitations that required manual handling. Originally teased as “React Forget” at React Conference, this feature can now automatically detect potential performance issues and address them. By analyzing component structures and dependencies, the compiler manages memoization and re-rendering for you, reducing the need for optimization techniques like memo(), useMemo(), and useCallback(). This brings not only speed and efficiency but also a simpler process for developers.

2. Actions

When interacting with data — whether you’re fetching it to display to users or mutating it to update a database — React 19 introduces a powerful new feature: Actions. According to the React 19 Beta Blog Post, any functions that use async transitions are referred to as “actions.” They allow you to perform data mutations in a more native React way, streamlining the process.

Form Submission: One of the most common scenarios for using Actions is when submitting a form. In React 19, you can pass a function to the action prop of a form element, much like how you previously used an onSubmit handler. This function handles form submission, validation, and other logic without needing an endpoint string.

3. useActionState Hook

React 19 also introduces the useActionState hook. This hook tracks the component’s state, pending status, and provides a wrapped action function that can be used in forms or other places where data mutations are needed. Unlike traditional event handlers, Actions don’t receive an event object. Instead, they directly access the form data — allowing you to manage it without needing to track it in local state. Now we have access all the information without tracking it in a local state and you can get rid of the input state, value prop. Here how it works:

<form action={handleFormSubmit}>
  <input 
    type="text"
    name="userName"
    required
  />
  <button type="submit">Submit</button>
</form>

async function handleFormSubmit(formData) {
  try {
    const updatedUserName = await saveUserNameToDb(formData.get("userName"));
    setUserName(updatedUserName);
  } catch (error) {
    console.error(error.message);
  }
}

4. Error Handling and Loading States

Handling loading states and errors manually can complicate your code and degrade the user experience. React 19 simplifies this with the useActionState() hook, which automatically manages loading states and error handling. See how errors and loading states handled in React 18:

function UserProfile() {
  const [userName, setUserName] = useState(
    () => JSON.parse(localStorage.getItem("userName")) || "Guest"
  );
  const [isLoading, setIsLoading] = useState(false);
  const [errorMsg, setErrorMsg] = useState(null);

  async function handleFormSubmit(formData) {
    setIsLoading(true);
    setErrorMsg(null);
    try {
      const updatedName = await saveNameToDatabase(formData.get("userName"));
      setUserName(updatedName);
    } catch (error) {
      console.error(error.message);
      setErrorMsg(error);
    } finally {
      setIsLoading(false);
    }
  }
}

In the example above, it’s unclear to the user whether the application is loading, which isn’t ideal for user experience. To address this, developers often manually manage loading states. With useActionState(), handling errors and loading states becomes much simpler. Here’s how it works:

const [userName, handleNameUpdate, loadingStatus] = useActionState(
  updateUserName,
  JSON.parse(localStorage.getItem("userName")) || "Guest"
);

async function updateUserName(previousState, formData) {
  try {
    const updatedName = await saveNameToDatabase(formData.get("userName"));
    return updatedName;
  } catch (error) {
    console.error(error.message);
  }
}

return (
  <>
    <p className="current-user">
      Current User: <span>{userName}</span>
    </p>
    {loadingStatus && <p>Loading...</p>}
    <form action={handleNameUpdate}>
      <input type="text" name="userName" required />
      <button type="submit">Update</button>
    </form>
  </>
);

5. useOptimistic Hook

React 19 introduces the useOptimistic hook, enabling developers to create highly responsive UIs by providing real-time feedback to users. This feature is particularly useful when you want to give users the illusion of an instant response — like when they hit a “like” button — without waiting for the database to confirm the action. If an error occurs, the changes made with useOptimistic will be reverted, and you can display an error message. Here’s how you can use useOptimistic:

const [appState, handleUpdate, loading] = useActionState(
  modifyUserName,
  {
    error: null,
    userName: JSON.parse(localStorage.getItem("userName")) || "Guest"
  }
);

const [tempUserName, setTempUserName] = useOptimistic(appState.userName);

async function modifyUserName(previousState, formData) {
  setTempUserName(formData.get("userName"));
  try {
    const updatedUserName = await saveNameToDatabase(formData.get("userName"));
    return { userName: updatedUserName };
  } catch (error) {
    return { error, userName: previousState.userName };
  }
}

return (
  <>
    <p className="username">
      Current User: <span>{tempUserName}</span>
    </p>
    <form action={handleUpdate}>
      <input type="text" name="userName" required />
      <button type="submit">Update</button>
      {!loading && appState.error && (
        <p className="error">{appState.error.message}</p>
      )}
    </form>
  </>
);

6. useFormStatus Hook

React 19 introduces another useful hook: useFormStatus(). This hook acts as a context consumer for the nearest parent <form> element, providing you with real-time information about the current status of that form, such as whether it’s pending submission.

In this example, the submit button is disabled while the form is pending, enhancing user experience by preventing multiple submissions:

import { useFormStatus } from 'react-dom';

function SubmitButton() {
  const { pending } = useFormStatus();
  
  return <button type="submit" disabled={pending}>Submit</button>;
}

7. Refs as Props

With React 19, you no longer need to use forwardRef to access the ref prop, which previously required extra steps and wrapping. Now, you can directly access the ref prop without additional wrapping. This simplifies the process, making your code cleaner and more straightforward. Here’s an example:


return (
  <>
    <p className="user-info">
      Current User: <span>{temporaryUserName}</span>
    </p>
    <form action={handleNameUpdate}>
      <input type="text" name="userName" required />
      <CustomButton ref={buttonRef} type="submit">Update</CustomButton>
      {!loading && appState.error && <p className="error">{appState.error.message}</p>}
    </form>
  </>
);

// We were doing this before React 19
forwardRef(function CustomButton({ children, ...props }, buttonRef) {
  const { pending } = useFormStatus();
  return (
    <button {...props}>{pending ? "Submitting..." : children}</button>
  );
});

// Now we just pass it as a prop
forwardRef(function CustomButton({ children, ref, ...props }) {
  const { pending } = useFormStatus();
  return (
    <button {...props}>{pending ? "Submitting..." : children}</button>
  );
});

8. A New API: use

Yes, I see the confusion, use is not a hook, but it can be called during render and doesn’t follow the rules of hooks. For example, you can use use conditionally. The use API reads async resources and automatically suspends the component until the promise resolves, potentially replacing the need for useContext. React also allows us to wrap components with a Suspense boundary, which can suspend the component while waiting for async data to load, displaying a fallback UI like a loading indicator until the data is available.

Here’s how use simplifies typical database interactions, removing the need to wrap async functions with useEffect:

import { use, Suspense } from "react";

function App() {
  const fetchPromise = fetch("https://jsonplaceholder.typicode.com/posts/1").then(response => response.json());
  const data = use(fetchPromise);

  return (
    <>
      <pre>
        <code>
          {JSON.stringify(data, null, 2)}
        </code>
      </pre>
    </>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(
  <Suspense fallback={<div>Loading...</div>}>
    <App />
  </Suspense>
);

9. Support for Document Metadata use

React 19 introduces native support for handling document metadata, such as <title>, <meta>, and <link> tags, directly within your components. Previously, developers relied on additional libraries like React Helmet to manage this metadata. Now, with React 19, you can directly manage these tags without needing an external library, allowing React to automatically insert them into the DOM.

function ArticleContent({ content }) {
  return (
    <article>
      <h1>{content.title}</h1>
      <title>{content.title}</title>
      <meta name="author" content="Ezgi" />
      <link rel="author" href="https://twitter.com/" />
      <meta name="keywords" content={content.keywords} />
      <p>
        Support for Document Metadata
      </p>
    </article>
  );
}


10. Web Components and Custom Elements

React 19 now fully supports Web components and custom elements, making it easier to integrate them into your React applications. In the past, there were challenges with compatibility, especially with how React handled props versus attributes on custom elements. This update smooths out those issues, allowing seamless interaction between React and Web components. With this feature, developers can now effortlessly use Web components within React.

10. Web Components and Custom Elements

React 19 now fully supports Web components and custom elements, making it easier to integrate them into your React applications. In the past, there were challenges with compatibility, especially with how React handled props versus attributes on custom elements. This update smooths out those issues, allowing seamless interaction between React and Web components. With this feature, developers can now effortlessly use Web components within React.

Conclusion

Whether you’re optimizing performance or exploring new APIs, there’s plenty to discover in this release. Dive into the React 19 Beta, experiment with these features, and see how they can enhance your projects

— happy coding with React 19!

Aug 21, 2024

Resources:

React Dev Blog: https://react.dev/blog/2024/04/25/react-19