YYGod0120
React19-BETACategories: Technology   2024-05-26

React19

On April 25th, the React team officially announced the release of React 19 on NPM. Developers can now download and start using it.

Coincidentally, this blog is also built using Next.js, so we can take this opportunity to try out the changes introduced in React 19.

Prepare

The official recommendation is to first install the stable version of React 18.3 to identify any potential hidden issues before upgrading to version 19.After verifying that everything is correct, you can proceed to install React 19 (currently in BETA) and start using the new APIs.Upgrade Guide for React v19

Difference

After a long period of silence, React has taken community feedback into account, addressing many pain points and optimizing the framework. The highly anticipated React 19 has been released.

Major changes include the following:

  • React Compiler: Assists developers in automatically optimizing pages, reducing the need for, or even eliminating, useMemo and useCallback.
  • Actions:new<form>Tags and corresponding Hooks for form operations.
  • New Hooks:New operation hooks such asuse()and more
  • Document Metadata:You can now write Meta data directly within a single component.
  • Web components:React code now allows us to merge Web components.

React-Compiler

The React Compiler can be said to be the most exciting feature in version 19. It is a new compiler designed to help developers optimize React code.For example, the series of optimization hooks such as useMemo and useCallback were previously cumbersome to use and could even lead to negative optimization. React has introduced the Compiler to automatically handle the code, avoiding the phenomenon of negative optimization.

In simple terms, the Compiler caches every element and function in the component, and only re-caches them when changes occur. Otherwise, it continues to use the cached values.

This article focuses on usage rather than principles. For more details, please refer to this article:I've Got React Compiler Down Pat

To enable React Compiler, we first need to perform a check on our project.

1npx react-compiler-healthcheck
2

This script is mainly used to check: 1. How many components in the project can be successfully optimized: the more, the better. 2. Whether strict mode is used, as using it increases the success rate of optimization. 3. Whether third-party libraries incompatible with the Compiler are used.

The results of this framework check are as follows:react-server-componentsDifferent frameworks have different methods for using the Compiler. To enable the Compiler in Next.js, you need to first download the Next-canary version and babel-plugin-react-compiler.

1npm install next@canary babel-plugin-react-compiler

then in next.config.js:

1// next.config.js
2/** @type {import('next').NextConfig} */
3const nextConfig = {
4  experimental: {
5    reactCompiler: true,
6  },
7};
8
9module.exports = nextConfig;
10

you can start the compiler to optimize the project.After successful optimization, you will see Memo stars in React-dev-tool.react-server-components

It is worth mentioning that the Compiler is still in testing and has many issues, such as conflicts with i18n client components, so it is still under observation.

New Hooks

React 19 introduces many new Hooks, includinguse(),useOptimistic(),useFormStatus(), most of which are designed for Action, i.e.<form>tags.

Since I didn't use forms, I started with other new features first,like use()

Use to retrieve resource values, such as Promises or Context. Unlike other hooks, it can be used within if statements.

Its specific principles are as follows (taken from the official documentation):

When a Promise is called using the use API, it integrates with and Suspense error boundaries. When the Promise passed to use is in a pending state, the component calling use will suspend. If the component calling use is wrapped in a Suspense boundary, a fallback will be displayed. After resolving the Promise, the Suspense fallback will be replaced by the component rendering the data returned by the use API. If the Promise passed to use is rejected, the fallback of the nearest error boundary will be displayed.

Basic usage is as follows:

1const value = use(resource);
2

It is worth noting that:

  • use must be called in a Component or Hook.
  • It is preferable to create Promises in server components and pass them to client components, rather than creating Promises in client components. Promises created in client components are recreated each time they are rendered. Promises passed from server components to client components are stable when re-rendered.
  • Like useContext, use(context) always looks for the nearest context provider above the component calling it. It searches upwards and does not consider the context provider in the component from which use(context) is called.
  • When passing a Promise from a server component to a client component, its resolved value must be serializable in order to be passed between the server and client. Data types such as functions that cannot be serialized cannot be the resolved value of such a Promise.

Usage in this project is as follows:

1"use client";
2import { Suspense } from "react";
3import { GhostPointer } from "./GhostPointer";
4import { MyTypeWrite } from "./TypeWrite";
5import { DailyWord } from "@/utils/getDailyWord";
6import ErrorBoundary from "./ErrorBoundary";
7
8export function Banner({
9  language,
10  isGetDailyWord,
11  wordsFetch,
12}: {
13  wordsFetch?: Promise<DailyWord>;
14  language: string;
15  isGetDailyWord: boolean;
16}) {
17  return (
18    <ErrorBoundary
19      fallback={
20        <GhostPointer>
21          <span
22            style={{
23              display: "flex",
24              lineHeight: "250px",
25              fontSize: "4rem",
26              justifyContent: "center",
27              color: "white",
28            }}
29          >
30            ⚠️Something went wrong
31          </span>
32        </GhostPointer>
33      }
34    >
35      <Suspense
36        fallback={
37          <GhostPointer>
38            <span
39              style={{
40                display: "flex",
41                lineHeight: "250px",
42                fontSize: "4rem",
43                justifyContent: "center",
44                color: "white",
45              }}
46            >
47              Loading...
48            </span>
49          </GhostPointer>
50        }
51      >
52        <GhostPointer>
53          <MyTypeWrite
54            language={language}
55            wordsFetch={wordsFetch}
56            isGetDailyWord={isGetDailyWord}
57          />
58        </GhostPointer>
59      </Suspense>
60    </ErrorBoundary>
61  );
62}
63

Wrap the target component with ErrorBoundary and Suspense to provide corresponding UI during resolution and after resolution failure. Then pass the wordsFetch function from the server and resolve it using use.

1//layout
2import { Banner } from "../components/Banner";
3import { getDailyWord } from "@/utils/getDailyWord";
4
5export default async function FrontLayout({
6  children,
7  params: { language },
8}: {
9  children: React.ReactNode;
10  params: { language: string };
11}) {
12  const wordsFetch = getDailyWord();
13  return (
14    <div className="flex flex-col items-center">
15      <div className="w-[100vw]">
16        <Banner
17          language={language}
18          isGetDailyWord={true}
19          wordsFetch={wordsFetch}
20        ></Banner>
21      </div>
22      <section className="w-full">{children}</section>
23    </div>
24  );
25}
26//TypeWrite
27("use client");
28import { usePathname } from "next/navigation";
29import { ReactTyped } from "react-typed";
30import { getDailyWord } from "@/utils/getDailyWord";
31import { Suspense, use, useState } from "react";
32import { DailyWord } from "@/utils/getDailyWord";
33import { splitPathname } from "@/utils/dealPathname";
34import { useTranslation } from "@/app/i18n/client";
35export function MyTypeWrite({
36  language,
37  isGetDailyWord,
38  wordsFetch,
39}: {
40  language: string;
41  isGetDailyWord: boolean;
42  wordsFetch?: Promise<DailyWord>;
43}) {
44  let word;
45  const pathName = usePathname();
46  const title = splitPathname(pathName);
47  const { t } = useTranslation(language, "translations");
48  if (isGetDailyWord && wordsFetch) {
49    const words = use(wordsFetch);
50    word = language === "zh-CN" ? words.note : words.content;
51  }
52  return (
53    <ReactTyped
54      strings={!word ? [t(title)] : [word]}
55      typeSpeed={50}
56      style={{
57        display: "flex",
58        lineHeight: "250px",
59        fontSize: "4rem",
60        justifyContent: "center",
61        color: "white",
62      }}
63    />
64  );
65}
66

The final effect can be seen on the blog homepage (code is stored on GitHub).

Other

React 19's updates go far beyond this. I have only used these two methods so far.

There are still many hooks related to optimistic updates, form operations, and more that I have yet to use.

The much-criticized Ref forwarding has also been optimized.

Error messages are now more user-friendly, and more.

I will gradually start using React 19 in production in the future.

React 19 Official Blog

About USE

About Compiler

© 2023 - 2024
githubYYGod0120