react19-patterns

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React 19 Patterns

React 19 模式

Overview

概述

This project uses React 19 with the React Compiler enabled. This changes how you should write React code, especially around Context and optimization.
本项目启用了 React Compiler 的 React 19,这会改变你编写 React 代码的方式,尤其是在 Context 和优化方面。

React 19 Context Pattern

React 19 Context 模式

Use Shorthand Syntax

使用简写语法

React 19 introduces shorthand syntax for Context providers.
✅ Correct (React 19):
tsx
<MyContext value={someValue}>
  <ChildComponents />
</MyContext>
❌ Incorrect (Old pattern):
tsx
<MyContext.Provider value={someValue}>
  <ChildComponents />
</MyContext.Provider>
React 19 为 Context 提供者引入了简写语法。
✅ 正确写法(React 19):
tsx
<MyContext value={someValue}>
  <ChildComponents />
</MyContext>
❌ 错误写法(旧模式):
tsx
<MyContext.Provider value={someValue}>
  <ChildComponents />
</MyContext.Provider>

Use
use()
Hook Instead of
useContext()

使用
use()
钩子替代
useContext()

React 19 introduces the
use()
hook for consuming context.
✅ Correct (React 19):
tsx
import { use } from "react";
import { MyContext } from "./MyContext";

function MyComponent() {
  const value = use(MyContext);
  return <div>{value}</div>;
}
❌ Incorrect (Old pattern):
tsx
import { useContext } from "react";
import { MyContext } from "./MyContext";

function MyComponent() {
  const value = useContext(MyContext);
  return <div>{value}</div>;
}
React 19 引入了
use()
钩子用于消费 Context。
✅ 正确写法(React 19):
tsx
import { use } from "react";
import { MyContext } from "./MyContext";

function MyComponent() {
  const value = use(MyContext);
  return <div>{value}</div>;
}
❌ 错误写法(旧模式):
tsx
import { useContext } from "react";
import { MyContext } from "./MyContext";

function MyComponent() {
  const value = useContext(MyContext);
  return <div>{value}</div>;
}

React Compiler Enabled

启用 React Compiler

No Manual Memoization Needed

无需手动 memoization

The React Compiler automatically optimizes components and handles memoization. Do not use manual memoization patterns.
✅ Correct (React Compiler handles it):
tsx
function MyComponent({ items }) {
  // React Compiler automatically memoizes this computation
  const filteredItems = items.filter((item) => item.active);

  // React Compiler automatically stabilizes this function reference
  const handleClick = (id) => {
    console.log(id);
  };

  return (
    <div>
      {filteredItems.map((item) => (
        <button key={item.id} onClick={() => handleClick(item.id)}>
          {item.name}
        </button>
      ))}
    </div>
  );
}
❌ Incorrect (Manual memoization not needed):
tsx
import { useMemo, useCallback, memo } from "react";

function MyComponent({ items }) {
  // ❌ Don't use useMemo - React Compiler handles this
  const filteredItems = useMemo(
    () => items.filter((item) => item.active),
    [items],
  );

  // ❌ Don't use useCallback - React Compiler handles this
  const handleClick = useCallback((id) => {
    console.log(id);
  }, []);

  return <div>...</div>;
}

// ❌ Don't use memo() - React Compiler handles this
export default memo(MyComponent);
React Compiler 会自动优化组件并处理 memoization。请勿使用手动 memoization 模式。
✅ 正确写法(由 React Compiler 处理):
tsx
function MyComponent({ items }) {
  // React Compiler 会自动缓存该计算结果
  const filteredItems = items.filter((item) => item.active);

  // React Compiler 会自动稳定该函数引用
  const handleClick = (id) => {
    console.log(id);
  };

  return (
    <div>
      {filteredItems.map((item) => (
        <button key={item.id} onClick={() => handleClick(item.id)}>
          {item.name}
        </button>
      ))}
    </div>
  );
}
❌ 错误写法(无需手动 memoization):
tsx
import { useMemo, useCallback, memo } from "react";

function MyComponent({ items }) {
  // ❌ 不要使用 useMemo - React Compiler 会处理
  const filteredItems = useMemo(
    () => items.filter((item) => item.active),
    [items],
  );

  // ❌ 不要使用 useCallback - React Compiler 会处理
  const handleClick = useCallback((id) => {
    console.log(id);
  }, []);

  return <div>...</div>;
}

// ❌ 不要使用 memo() - React Compiler 会处理
export default memo(MyComponent);

React 19 ViewTransition + Suspense Pattern

React 19 ViewTransition + Suspense 模式

The Hydration Issue

水合问题

When using
<ViewTransition>
to wrap content that includes Suspense boundaries, you may encounter hydration errors if some children are in Suspense while others are not.
Error Message:
A tree hydrated but some attributes of the server rendered HTML didn't match the client properties.
This won't be patched up. This can happen if a SSR-ed Client Component used...
Specifically: style={{view-transition-name:"_T_0_"}}
Root Cause:
  • ViewTransition uses a "just-in-time" mechanism to apply
    view-transition-name
    styles only when transitions trigger
  • During SSR hydration, content reveals from non-Suspense children trigger ViewTransition activation
  • This causes the client to apply styles during hydration that weren't in the server HTML
  • React detects the mismatch and logs a hydration warning
当使用
<ViewTransition>
包裹包含 Suspense 边界的内容时,如果部分子组件处于 Suspense 状态而其他组件不是,可能会遇到水合错误。
错误信息:
A tree hydrated but some attributes of the server rendered HTML didn't match the client properties.
This won't be patched up. This can happen if a SSR-ed Client Component used...
Specifically: style={{view-transition-name:"_T_0_"}}
根本原因:
  • ViewTransition 使用“即时”机制,仅在触发过渡时才应用
    view-transition-name
    样式
  • 在 SSR 水合过程中,非 Suspense 子组件的内容显示会触发 ViewTransition 激活
  • 这会导致客户端在水合期间应用服务器 HTML 中不存在的样式
  • React 检测到不匹配并记录水合警告

The Solution: Consistent Suspense Boundaries

解决方案:统一 Suspense 边界

✅ Correct (All content in Suspense):
tsx
<ViewTransition>
  <Suspense fallback={<HeaderSkeleton />}>
    <Header />
  </Suspense>
  <Suspense fallback={null}>{children}</Suspense>
</ViewTransition>
❌ Incorrect (Mixed Suspense/non-Suspense):
tsx
<ViewTransition>
  <Suspense fallback={<HeaderSkeleton />}>
    <Header />
  </Suspense>
  {children} {/* NOT in Suspense - causes hydration error! */}
</ViewTransition>
Alternative (Suspense outside ViewTransition):
tsx
<Suspense fallback={<LoadingSkeleton />}>
  <ViewTransition>
    <Header />
    {children}
  </ViewTransition>
</Suspense>
Note: This alternative forces all content into the same loading state, which may not be desirable if Header and children should load independently.
✅ 正确写法(所有内容都在 Suspense 中):
tsx
<ViewTransition>
  <Suspense fallback={<HeaderSkeleton />}>
    <Header />
  </Suspense>
  <Suspense fallback={null}>{children}</Suspense>
</ViewTransition>
❌ 错误写法(混合 Suspense/非 Suspense):
tsx
<ViewTransition>
  <Suspense fallback={<HeaderSkeleton />}>
    <Header />
  </Suspense>
  {children} {/* 不在 Suspense 中 - 会导致水合错误! */}
</ViewTransition>
替代方案(Suspense 在 ViewTransition 外部):
tsx
<Suspense fallback={<LoadingSkeleton />}>
  <ViewTransition>
    <Header />
    {children}
  </ViewTransition>
</Suspense>
注意:此替代方案会强制所有内容进入相同的加载状态,如果 Header 和子组件需要独立加载,这可能不是理想选择。

Why This Fixes It

该修复的原理

By wrapping all children in Suspense boundaries:
  • Content reveals are coordinated through React's Suspense mechanism
  • ViewTransition doesn't activate prematurely during hydration
  • Server and client rendering remain consistent
  • No hydration mismatch occurs
通过将所有子组件包裹在 Suspense 边界中:
  • 内容显示会通过 React 的 Suspense 机制进行协调
  • ViewTransition 不会在水合期间过早激活
  • 服务器和客户端渲染保持一致
  • 不会出现水合不匹配

Key Rules

核心规则

  1. Context Shorthand: Always use
    <Context value={...}>
    instead of
    <Context.Provider value={...}>
  2. use() Hook: Always use
    use(Context)
    instead of
    useContext(Context)
  3. No useMemo: React Compiler automatically memoizes expensive computations
  4. No useCallback: React Compiler automatically stabilizes function references
  5. No memo(): React Compiler automatically optimizes component re-renders
  6. Trust the Compiler: Let React Compiler handle optimization instead of manual patterns
  7. ViewTransition + Suspense: When using ViewTransition with Suspense, ensure all children are within Suspense boundaries to prevent hydration errors
  1. Context 简写:始终使用
    <Context value={...}>
    替代
    <Context.Provider value={...}>
  2. use() 钩子:始终使用
    use(Context)
    替代
    useContext(Context)
  3. 禁用 useMemo:React Compiler 会自动缓存昂贵的计算
  4. 禁用 useCallback:React Compiler 会自动稳定函数引用
  5. 禁用 memo():React Compiler 会自动优化组件重渲染
  6. 信任编译器:让 React Compiler 处理优化,而非手动模式
  7. ViewTransition + Suspense:当结合使用 ViewTransition 和 Suspense 时,确保所有子组件都在 Suspense 边界内,以避免水合错误

When Manual Optimization Might Be Needed

可能需要手动优化的场景

In rare cases, you might still need manual optimization:
  • External library integration that expects stable references
  • Performance profiling shows a specific issue that React Compiler doesn't catch
Always profile first before adding manual optimizations. The React Compiler is very effective.
在极少数情况下,你可能仍需要手动优化:
  • 集成外部库,该库需要稳定的引用
  • 性能分析显示 React Compiler 未解决的特定问题
添加手动优化前务必先进行性能分析,React Compiler 的优化效果非常出色。