ReactTypeScript开发Skill react-dev

这个技能专注于使用TypeScript进行React前端开发,提供类型安全保证,覆盖React 18-19的特性,如通用组件、事件处理、服务器组件和路由集成。关键词:React, TypeScript, 前端开发, 类型安全, 组件开发, 钩子, 服务器组件, 路由集成。

前端开发 0 次安装 0 次浏览 更新于 3/21/2026

名称: react-dev 版本: 1.0.0 description: 此技能应在使用TypeScript构建React组件、类型化钩子、处理事件或提及React TypeScript、React 19、服务器组件时使用。涵盖React 18-19的类型安全模式,包括通用组件、正确的事件类型和路由集成(TanStack Router、React Router)。

React TypeScript

类型安全的React = 编译时保证 = 自信的重构。

<when_to_use>

  • 构建类型化的React组件
  • 实现通用组件
  • 类型化事件处理程序、表单、引用
  • 使用React 19特性(Actions、服务器组件、use())
  • 路由集成(TanStack Router、React Router)
  • 正确类型化的自定义钩子

不适用于:非React的TypeScript、纯JS React

</when_to_use>

<react_19_changes>

React 19的破坏性变化需要迁移。关键模式:

ref作为prop - forwardRef已弃用:

// React 19 - ref作为常规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:

import { useActionState } from 'react';

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

function Form() {
  const [state, formAction, isPending] = useActionState(submitAction, {});
  return <form action={formAction}>...</form>;
}

use() - 解包promises/context:

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>

Props - 扩展原生元素:

type ButtonProps = {
  variant: 'primary' | 'secondary';
} & React.ComponentPropsWithoutRef<'button'>;

function Button({ variant, children, ...props }: ButtonProps) {
  return <button className={variant} {...props}>{children}</button>;
}

Children类型化

type Props = {
  children: React.ReactNode;          // 任何可渲染内容
  icon: React.ReactElement;           // 单个元素
  render: (data: T) => React.ReactNode;  // 渲染prop
};

判别联合类型用于变体props:

type ButtonProps =
  | { variant: 'link'; href: string }
  | { variant: 'button'; onClick: () => void };

function Button(props: ButtonProps) {
  if (props.variant === 'link') {
    return <a href={props.href}>链接</a>;
  }
  return <button onClick={props.onClick}>按钮</button>;
}

</component_patterns>

<event_handlers>

使用特定事件类型进行精确目标类型化:

// 鼠标
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 - 显式用于联合类型/null:

const [user, setUser] = useState<User | null>(null);
const [status, setStatus] = useState<'idle' | 'loading'>('idle');

useRef - null用于DOM,值用于可变引用:

const inputRef = useRef<HTMLInputElement>(null);  // DOM - 使用 ?.
const countRef = useRef<number>(0);               // 可变 - 直接访问

useReducer - 判别联合类型用于actions:

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;
  }
}

自定义钩子 - 元组返回使用as const:

function useToggle(initial = false) {
  const [value, setValue] = useState(initial);
  const toggle = () => setValue(v => !v);
  return [value, toggle] as const;
}

useContext - null守卫模式:

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>

通用组件从props推断类型 - 调用点无需手动注解。

模式 - keyof T用于列键,渲染props用于自定义渲染:

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>
  );
}

约束泛型用于必需属性:

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服务器组件在服务器上运行,可以是异步的。

异步数据获取

export default async function UserPage({ params }: { params: { id: string } }) {
  const user = await fetchUser(params.id);
  return <div>{user.name}</div>;
}

服务器Actions - 'use server’用于突变:

'use server';

export async function updateUser(userId: string, formData: FormData) {
  await db.user.update({ where: { id: userId }, data: { ... } });
  revalidatePath(`/users/${userId}`);
}

客户端 + 服务器Action

'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传递

// 服务器:传递promise而不await
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验证的编译时类型安全:

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 - 使用Framework Mode的自动类型生成:

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模式,react-router.md 了解React Router模式。

</routing>

<rules>

总是:

  • 特定事件类型(MouseEvent、ChangeEvent等)
  • 显式useState用于联合类型/null
  • ComponentPropsWithoutRef用于原生元素扩展
  • 判别联合类型用于变体props
  • as const用于元组返回
  • ref作为prop在React 19中(无forwardRef)
  • useActionState用于表单actions
  • 类型安全的路由模式(见路由部分)

从不:

  • any用于事件处理程序
  • JSX.Element用于children(使用ReactNode)
  • forwardRef在React 19+中
  • useFormState(已弃用)
  • 忘记DOM refs的null处理
  • 在同一文件中混合服务器/客户端组件
  • 当传递到use()时await promises

</rules>

<references>

</references>