react-dev

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React TypeScript

React TypeScript

Type-safe React = compile-time guarantees = confident refactoring.
<when_to_use>
  • Building typed React components
  • Implementing generic components
  • Typing event handlers, forms, refs
  • Using React 19 features (Actions, Server Components, use())
  • Router integration (TanStack Router, React Router)
  • Custom hooks with proper typing
NOT for: non-React TypeScript, vanilla JS React
</when_to_use>
<react_19_changes>
React 19 breaking changes require migration. Key patterns:
ref as prop - forwardRef deprecated:
typescript
// React 19 - ref as regular prop
type ButtonProps = {
  ref?: React.Ref<HTMLButtonElement>;
} & React.ComponentPropsWithoutRef<'button'>;

function Button({ ref, children, ...props }: ButtonProps) {
  return <button ref={ref} {...props}>{children}</button>;
}
useActionState - replaces useFormState:
typescript
import { useActionState } from 'react';

type FormState = { errors?: string[]; success?: boolean };

function Form() {
  const [state, formAction, isPending] = useActionState(submitAction, {});
  return <form action={formAction}>...</form>;
}
use() - unwraps promises/context:
typescript
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
  const user = use(userPromise); // Suspends until resolved
  return <div>{user.name}</div>;
}
See react-19-patterns.md for useOptimistic, useTransition, migration checklist.
</react_19_changes>
<component_patterns>
Props - extend native elements:
typescript
type ButtonProps = {
  variant: 'primary' | 'secondary';
} & React.ComponentPropsWithoutRef<'button'>;

function Button({ variant, children, ...props }: ButtonProps) {
  return <button className={variant} {...props}>{children}</button>;
}
Children typing:
typescript
type Props = {
  children: React.ReactNode;          // Anything renderable
  icon: React.ReactElement;           // Single element
  render: (data: T) => React.ReactNode;  // Render prop
};
Discriminated unions for variant props:
typescript
type ButtonProps =
  | { variant: 'link'; href: string }
  | { variant: 'button'; onClick: () => void };

function Button(props: ButtonProps) {
  if (props.variant === 'link') {
    return <a href={props.href}>Link</a>;
  }
  return <button onClick={props.onClick}>Button</button>;
}
</component_patterns>
<event_handlers>
Use specific event types for accurate target typing:
typescript
// Mouse
function handleClick(e: React.MouseEvent<HTMLButtonElement>) {
  e.currentTarget.disabled = true;
}

// Form
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
  e.preventDefault();
  const formData = new FormData(e.currentTarget);
}

// Input
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
  console.log(e.target.value);
}

// Keyboard
function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
  if (e.key === 'Enter') e.currentTarget.blur();
}
See event-handlers.md for focus, drag, clipboard, touch, wheel events.
</event_handlers>
<hooks_typing>
useState - explicit for unions/null:
typescript
const [user, setUser] = useState<User | null>(null);
const [status, setStatus] = useState<'idle' | 'loading'>('idle');
useRef - null for DOM, value for mutable:
typescript
const inputRef = useRef<HTMLInputElement>(null);  // DOM - use ?.
const countRef = useRef<number>(0);               // Mutable - direct access
useReducer - discriminated unions for actions:
typescript
type Action =
  | { type: 'increment' }
  | { type: 'set'; payload: number };

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'set': return { ...state, count: action.payload };
    default: return state;
  }
}
Custom hooks - tuple returns with as const:
typescript
function useToggle(initial = false) {
  const [value, setValue] = useState(initial);
  const toggle = () => setValue(v => !v);
  return [value, toggle] as const;
}
useContext - null guard pattern:
typescript
const UserContext = createContext<User | null>(null);

function useUser() {
  const user = useContext(UserContext);
  if (!user) throw new Error('useUser outside UserProvider');
  return user;
}
See hooks.md for useCallback, useMemo, useImperativeHandle, useSyncExternalStore.
</hooks_typing>
<generic_components>
Generic components infer types from props - no manual annotations at call site.
Pattern - keyof T for column keys, render props for custom rendering:
typescript
type Column<T> = {
  key: keyof T;
  header: string;
  render?: (value: T[keyof T], item: T) => React.ReactNode;
};

type TableProps<T> = {
  data: T[];
  columns: Column<T>[];
  keyExtractor: (item: T) => string | number;
};

function Table<T>({ data, columns, keyExtractor }: TableProps<T>) {
  return (
    <table>
      <thead>
        <tr>{columns.map(col => <th key={String(col.key)}>{col.header}</th>)}</tr>
      </thead>
      <tbody>
        {data.map(item => (
          <tr key={keyExtractor(item)}>
            {columns.map(col => (
              <td key={String(col.key)}>
                {col.render ? col.render(item[col.key], item) : String(item[col.key])}
              </td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}
Constrained generics for required properties:
typescript
type HasId = { id: string | number };

function List<T extends HasId>({ items }: { items: T[] }) {
  return <ul>{items.map(item => <li key={item.id}>...</li>)}</ul>;
}
See generic-components.md for Select, List, Modal, FormField patterns.
</generic_components>
<server_components>
React 19 Server Components run on server, can be async.
Async data fetching:
typescript
export default async function UserPage({ params }: { params: { id: string } }) {
  const user = await fetchUser(params.id);
  return <div>{user.name}</div>;
}
Server Actions - 'use server' for mutations:
typescript
'use server';

export async function updateUser(userId: string, formData: FormData) {
  await db.user.update({ where: { id: userId }, data: { ... } });
  revalidatePath(`/users/${userId}`);
}
Client + Server Action:
typescript
'use client';

import { useActionState } from 'react';
import { updateUser } from '@/actions/user';

function UserForm({ userId }: { userId: string }) {
  const [state, formAction, isPending] = useActionState(
    (prev, formData) => updateUser(userId, formData), {}
  );
  return <form action={formAction}>...</form>;
}
use() for promise handoff:
typescript
// Server: pass promise without await
async function Page() {
  const userPromise = fetchUser('123');
  return <UserProfile userPromise={userPromise} />;
}

// Client: unwrap with use()
'use client';
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
  const user = use(userPromise);
  return <div>{user.name}</div>;
}
See server-components.md for parallel fetching, streaming, error boundaries.
</server_components>
<routing>
Both TanStack Router and React Router v7 provide type-safe routing solutions.
TanStack Router - Compile-time type safety with Zod validation:
typescript
import { createRoute } from '@tanstack/react-router';
import { z } from 'zod';

const userRoute = createRoute({
  path: '/users/$userId',
  component: UserPage,
  loader: async ({ params }) => ({ user: await fetchUser(params.userId) }),
  validateSearch: z.object({
    tab: z.enum(['profile', 'settings']).optional(),
    page: z.number().int().positive().default(1),
  }),
});

function UserPage() {
  const { user } = useLoaderData({ from: userRoute.id });
  const { tab, page } = useSearch({ from: userRoute.id });
  const { userId } = useParams({ from: userRoute.id });
}
React Router v7 - Automatic type generation with Framework Mode:
typescript
import type { Route } from "./+types/user";

export async function loader({ params }: Route.LoaderArgs) {
  return { user: await fetchUser(params.userId) };
}

export default function UserPage({ loaderData }: Route.ComponentProps) {
  const { user } = loaderData; // Typed from loader
  return <h1>{user.name}</h1>;
}
See tanstack-router.md for TanStack patterns and react-router.md for React Router patterns.
</routing> <rules>
ALWAYS:
  • Specific event types (MouseEvent, ChangeEvent, etc)
  • Explicit useState for unions/null
  • ComponentPropsWithoutRef for native element extension
  • Discriminated unions for variant props
  • as const for tuple returns
  • ref as prop in React 19 (no forwardRef)
  • useActionState for form actions
  • Type-safe routing patterns (see routing section)
NEVER:
  • any for event handlers
  • JSX.Element for children (use ReactNode)
  • forwardRef in React 19+
  • useFormState (deprecated)
  • Forget null handling for DOM refs
  • Mix Server/Client components in same file
  • Await promises when passing to use()
</rules> <references>
  • hooks.md - useState, useRef, useReducer, useContext, custom hooks
  • event-handlers.md - all event types, generic handlers
  • react-19-patterns.md - useActionState, use(), useOptimistic, migration
  • generic-components.md - Table, Select, List, Modal patterns
  • server-components.md - async components, Server Actions, streaming
  • tanstack-router.md - TanStack Router typed routes, search params, navigation
  • react-router.md - React Router v7 loaders, actions, type generation, forms
</references>
类型安全的React = 编译时保障 = 放心重构。
<when_to_use>
  • 构建类型化React组件
  • 实现泛型组件
  • 类型化事件处理程序、表单、refs
  • 使用React 19特性(Actions、Server Components、use())
  • 路由集成(TanStack Router、React Router)
  • 带正确类型定义的自定义Hooks
不适用场景:非React的TypeScript、原生JS React
</when_to_use>
<react_19_changes>
React 19的破坏性变更需要迁移。核心模式:
ref作为属性 - forwardRef已弃用:
typescript
// React 19 - ref as regular prop
type ButtonProps = {
  ref?: React.Ref<HTMLButtonElement>;
} & React.ComponentPropsWithoutRef<'button'>;

function Button({ ref, children, ...props }: ButtonProps) {
  return <button ref={ref} {...props}>{children}</button>;
}
useActionState - 替代useFormState:
typescript
import { useActionState } from 'react';

type FormState = { errors?: string[]; success?: boolean };

function Form() {
  const [state, formAction, isPending] = useActionState(submitAction, {});
  return <form action={formAction}>...</form>;
}
use() - 解析promise/上下文:
typescript
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
  const user = use(userPromise); // 挂起直到解析完成
  return <div>{user.name}</div>;
}
查看react-19-patterns.md了解useOptimistic、useTransition以及迁移清单。
</react_19_changes>
<component_patterns>
属性 - 扩展原生元素:
typescript
type ButtonProps = {
  variant: 'primary' | 'secondary';
} & React.ComponentPropsWithoutRef<'button'>;

function Button({ variant, children, ...props }: ButtonProps) {
  return <button className={variant} {...props}>{children}</button>;
}
Children类型定义
typescript
type Props = {
  children: React.ReactNode;          // 任何可渲染内容
  icon: React.ReactElement;           // 单个元素
  render: (data: T) => React.ReactNode;  // 渲染属性
};
变体属性的区分联合类型
typescript
type ButtonProps =
  | { variant: 'link'; href: string }
  | { variant: 'button'; onClick: () => void };

function Button(props: ButtonProps) {
  if (props.variant === 'link') {
    return <a href={props.href}>Link</a>;
  }
  return <button onClick={props.onClick}>Button</button>;
}
</component_patterns>
<event_handlers>
使用特定事件类型以获得准确的目标类型:
typescript
// 鼠标事件
function handleClick(e: React.MouseEvent<HTMLButtonElement>) {
  e.currentTarget.disabled = true;
}

// 表单事件
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
  e.preventDefault();
  const formData = new FormData(e.currentTarget);
}

// 输入事件
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
  console.log(e.target.value);
}

// 键盘事件
function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
  if (e.key === 'Enter') e.currentTarget.blur();
}
查看event-handlers.md了解焦点、拖拽、剪贴板、触摸、滚轮事件。
</event_handlers>
<hooks_typing>
useState - 联合类型/空值需显式定义:
typescript
const [user, setUser] = useState<User | null>(null);
const [status, setStatus] = useState<'idle' | 'loading'>('idle');
useRef - DOM元素用null,可变值用具体类型:
typescript
const inputRef = useRef<HTMLInputElement>(null);  // DOM元素 - 使用?.访问
const countRef = useRef<number>(0);               // 可变值 - 直接访问
useReducer - 动作使用区分联合类型:
typescript
type Action =
  | { type: 'increment' }
  | { type: 'set'; payload: number };

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'set': return { ...state, count: action.payload };
    default: return state;
  }
}
自定义Hooks - 元组返回使用as const:
typescript
function useToggle(initial = false) {
  const [value, setValue] = useState(initial);
  const toggle = () => setValue(v => !v);
  return [value, toggle] as const;
}
useContext - 空值防护模式:
typescript
const UserContext = createContext<User | null>(null);

function useUser() {
  const user = useContext(UserContext);
  if (!user) throw new Error('useUser必须在UserProvider内部使用');
  return user;
}
查看hooks.md了解useCallback、useMemo、useImperativeHandle、useSyncExternalStore。
</hooks_typing>
<generic_components>
泛型组件从属性推断类型 - 调用时无需手动注解。
模式 - 用keyof T作为列键,渲染属性用于自定义渲染:
typescript
type Column<T> = {
  key: keyof T;
  header: string;
  render?: (value: T[keyof T], item: T) => React.ReactNode;
};

type TableProps<T> = {
  data: T[];
  columns: Column<T>[];
  keyExtractor: (item: T) => string | number;
};

function Table<T>({ data, columns, keyExtractor }: TableProps<T>) {
  return (
    <table>
      <thead>
        <tr>{columns.map(col => <th key={String(col.key)}>{col.header}</th>)}</tr>
      </thead>
      <tbody>
        {data.map(item => (
          <tr key={keyExtractor(item)}>
            {columns.map(col => (
              <td key={String(col.key)}>
                {col.render ? col.render(item[col.key], item) : String(item[col.key])}
              </td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}
带约束的泛型用于必填属性:
typescript
type HasId = { id: string | number };

function List<T extends HasId>({ items }: { items: T[] }) {
  return <ul>{items.map(item => <li key={item.id}>...</li>)}</ul>;
}
查看generic-components.md了解Select、List、Modal、FormField的实现模式。
</generic_components>
<server_components>
React 19 Server Components在服务器运行,支持异步。
异步数据获取
typescript
export default async function UserPage({ params }: { params: { id: string } }) {
  const user = await fetchUser(params.id);
  return <div>{user.name}</div>;
}
Server Actions - 用'use server'标记 mutations:
typescript
'use server';

export async function updateUser(userId: string, formData: FormData) {
  await db.user.update({ where: { id: userId }, data: { ... } });
  revalidatePath(`/users/${userId}`);
}
客户端+Server Action
typescript
'use client';

import { useActionState } from 'react';
import { updateUser } from '@/actions/user';

function UserForm({ userId }: { userId: string }) {
  const [state, formAction, isPending] = useActionState(
    (prev, formData) => updateUser(userId, formData), {}
  );
  return <form action={formAction}>...</form>;
}
use()用于Promise传递
typescript
// 服务器端:不使用await直接传递promise
async function Page() {
  const userPromise = fetchUser('123');
  return <UserProfile userPromise={userPromise} />;
}

// 客户端:用use()解析
'use client';
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
  const user = use(userPromise);
  return <div>{user.name}</div>;
}
查看server-components.md了解并行获取、流式传输、错误边界。
</server_components>
<routing>
TanStack Router和React Router v7都提供类型安全的路由解决方案。
TanStack Router - 结合Zod验证实现编译时类型安全:
typescript
import { createRoute } from '@tanstack/react-router';
import { z } from 'zod';

const userRoute = createRoute({
  path: '/users/$userId',
  component: UserPage,
  loader: async ({ params }) => ({ user: await fetchUser(params.userId) }),
  validateSearch: z.object({
    tab: z.enum(['profile', 'settings']).optional(),
    page: z.number().int().positive().default(1),
  }),
});

function UserPage() {
  const { user } = useLoaderData({ from: userRoute.id });
  const { tab, page } = useSearch({ from: userRoute.id });
  const { userId } = useParams({ from: userRoute.id });
}
React Router v7 - 框架模式自动生成类型:
typescript
import type { Route } from "./+types/user";

export async function loader({ params }: Route.LoaderArgs) {
  return { user: await fetchUser(params.userId) };
}

export default function UserPage({ loaderData }: Route.ComponentProps) {
  const { user } = loaderData; // 类型从loader自动推断
  return <h1>{user.name}</h1>;
}
查看tanstack-router.md了解TanStack Router模式,react-router.md了解React Router模式。
</routing> <rules>
必须遵循:
  • 使用特定事件类型(MouseEvent、ChangeEvent等)
  • 联合类型/空值显式定义useState
  • 扩展原生元素使用ComponentPropsWithoutRef
  • 变体属性使用区分联合类型
  • 元组返回使用as const
  • React 19中ref作为属性(不使用forwardRef)
  • 表单动作使用useActionState
  • 类型安全的路由模式(见路由章节)
严禁:
  • 事件处理程序使用any
  • 用JSX.Element定义children(使用ReactNode)
  • React 19+中使用forwardRef
  • 使用已弃用的useFormState
  • 忽略DOM refs的空值处理
  • 同一文件混合Server/Client组件
  • 传递给use()的Promise使用await
</rules> <references>
  • hooks.md - useState、useRef、useReducer、useContext、自定义Hooks
  • event-handlers.md - 所有事件类型、泛型处理程序
  • react-19-patterns.md - useActionState、use()、useOptimistic、迁移指南
  • generic-components.md - Table、Select、List、Modal模式
  • server-components.md - 异步组件、Server Actions、流式传输
  • tanstack-router.md - TanStack Router类型化路由、搜索参数、导航
  • react-router.md - React Router v7加载器、动作、类型生成、表单
</references>