React

涵盖 React 19 全栈开发规范,含架构、状态管理与性能优化最佳实践。

已扫描
适合谁
前端工程师、全栈开发人员
不适合谁
初学者入门者、非前端开发人员
国内可用性
需网络配置。可能需要网络配置或第三方服务可访问。
安装难度
新手友好(★☆☆)。基于终端操作、依赖、API Key 和本地环境要求的初步判断。

安装与下载

openclaw skills install @ivangdavila/react

Skill 说明

命令、参数、文件名以原文为准

React

生产级别的 React 工程实践。此技能将改变你构建 React 应用的方式——从组件架构到部署全流程。

何时使用

  • 构建 React 组件、页面或功能
  • 实现状态管理(useState、Context、Zustand、TanStack Query)
  • 使用 React 19(服务器组件、use()、Actions)
  • 性能优化(memo、lazy、Suspense)
  • 调试渲染问题、无限循环、过期闭包
  • 设置项目架构和文件目录结构

架构决策

在编写代码前,请先做出以下决定:

决策可选项默认值
渲染方式SPA / SSR / 静态 / 混合SSR(Next.js)
服务端状态TanStack Query / SWR / use()TanStack Query
客户端状态useState / Zustand / JotaiZustand(若需共享)
样式方案Tailwind / CSS Modules / styledTailwind
表单处理React Hook Form + Zod / 原生表单RHF + Zod

规则: 服务端状态(API 数据)与客户端状态(UI 状态)必须分离。绝不混合使用。

组件规范

// ✅ 正确的编写模式
export function UserCard({ user, onEdit }: UserCardProps) {
  // 1. 钩子优先(始终如此)
  const [isOpen, setIsOpen] = useState(false)

  // 2. 派生状态(不通过 useEffect 实现)
  const fullName = `${user.firstName} ${user.lastName}`

  // 3. 事件处理器
  const handleEdit = useCallback(() => onEdit(user.id), [onEdit, user.id])

  // 4. 提前返回
  if (!user) return null

  // 5. JSX(不超过 50 行)
  return (...)
}
规则原因
仅使用命名导出支持重构安全性和 IDE 支持
导出 Props 接口可复用、可文档化
JSX 最多 50 行超过则提取为独立组件
单个文件最多 300 行超过则拆分为多个组件
钩子置于顶部符合 React 规范,逻辑更清晰

状态管理

是否来自 API?
├─ 是 → TanStack Query(非 Redux,非 Zustand)
└─ 否 → 是否在多个组件间共享?
    ├─ 是 → Zustand(简单场景)或 Context(更新频率低)
    └─ 否 → useState

TanStack Query(服务端状态)

// 查询键工厂 — 防止键名拼写错误
export const userKeys = {
  all: ['users'] as const,
  detail: (id: string) => [...userKeys.all, id] as const,
}

export function useUser(id: string) {
  return useQuery({
    queryKey: userKeys.detail(id),
    queryFn: () => fetchUser(id),
    staleTime: 5 * 60 * 1000, // 5 分钟
  })
}

Zustand(客户端状态)

// 简洁的状态存储,单一职责
export const useUIStore = create<UIState>()((set) => ({
  sidebarOpen: true,
  toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
}))

// 始终使用选择器 — 避免不必要的重新渲染
const isOpen = useUIStore((s) => s.sidebarOpen)

React 19

服务器组件(Next.js App Router 默认启用)

// 服务器组件 — 在服务端运行,不会发送任何 JS 到客户端
async function ProductList() {
  const products = await db.products.findMany() // 直接访问数据库
  return <ul>{products.map(p => <ProductCard key={p.id} product={p} />)}</ul>
}

// 客户端组件 — 必须添加 'use client' 指令
'use client'
function AddToCartButton({ productId }: { productId: string }) {
  const [loading, setLoading] = useState(false)
  return <button onClick={() => addToCart(productId)}>添加</button>
}
服务器组件客户端组件
支持 async/await ✅支持 useState ✅
支持直接数据库访问 ✅支持 onClick ✅
不增加打包体积会增加打包体积
不支持 useState ❌不支持 async ❌

use() 钩子

// 在渲染中读取 Promise(配合 Suspense 使用)
function Comments({ promise }: { promise: Promise<Comment[]> }) {
  const comments = use(promise) // 渲染时暂停,直到解析完成
  return <ul>{comments.map(c => <li key={c.id}>{c.text}</li>)}</ul>
}

useActionState(表单处理)

'use client'
async function submitAction(prev: State, formData: FormData) {
  'use server'
  // ... 服务端逻辑
  return { success: true }
}

function Form() {
  const [state, action, pending] = useActionState(submitAction, {})
  return (
    <form action={action}>
      <input name="email" disabled={pending} />
      <button disabled={pending}>{pending ? '保存中...' : '保存'}</button>
      {state.error && <p>{state.error}</p>}
    </form>
  )
}

性能优化

优先级技术影响程度
P0路由级代码分割🔴 高
P0图片优化(next/image)🔴 高
P1长列表虚拟化(tanstack-virtual)🟡 中等
P1消除昂贵操作的频繁触发(防抖)🟡 中等
P2对计算密集型组件使用 React.memo🟢 低至中等
P2对复杂计算使用 useMemo🟢 低至中等

React Compiler(React 19+): 自动进行记忆化处理。移除手动的 memo/useMemo/useCallback。

常见陷阱

渲染相关陷阱

// ❌ 当 count 为 0 时会渲染 "0"
{count && <Component />}

// ✅ 明确判断布尔值
{count > 0 && <Component />}
// ❌ 修改状态 — React 无法检测到变化
array.push(item)
setArray(array)

// ✅ 创建新引用
setArray([...array, item])
// ❌ 每次渲染都生成新 key — 导致组件被销毁重建
<Item key={Math.random()} />

// ✅ 使用稳定 key
<Item key={item.id} />

钩子相关陷阱

// ❌ useEffect 不能是 async 函数
useEffect(async () => { ... }, [])

// ✅ 在内部定义异步函数
useEffect(() => {
  async function load() { ... }
  load()
}, [])
// ❌ 缺少清理函数 — 内存泄漏
useEffect(() => {
  const sub = subscribe()
}, [])

// ✅ 返回清理函数
useEffect(() => {
  const sub = subscribe()
  return () => sub.unsubscribe()
}, [])
// ❌ 依赖项中包含对象 — 每次渲染都会触发
useEffect(() => { ... }, [{ id: 1 }])

// ✅ 提取基础类型或使用 memoize
useEffect(() => { ... }, [id])

数据获取陷阱

// ❌ 串行请求 — 效率低下
const users = await fetchUsers()
const orders = await fetchOrders()

// ✅ 并行请求
const [users, orders] = await Promise.all([fetchUsers(), fetchOrders()])

tsx

// ❌ 竞态条件 — 无法中止请求

useEffect(() => {

fetch(url).then(setData)

}, [url])

// ✅ 使用 AbortController 中止请求

useEffect(() => {

const controller = new AbortController()

fetch(url, { signal: controller.signal }).then(setData)

return () => controller.abort()

}, [url])

## AI 常见错误避免

AI 助手在使用 React 时常见的错误:

| 错误 | 正确模式 |
|------|----------|
| 使用 useEffect 处理派生状态 | 直接计算:`const x = a + b` |
| 使用 Redux 管理 API 数据 | 使用 TanStack Query 管理服务器状态 |
| 使用默认导出 | 使用命名导出:`export function X` |
| 动态列表中使用索引作为 key | 使用稳定 ID:`key={item.id}` |
| 在 useEffect 中发起请求 | 使用 TanStack Query 或 loader 模式 |
| 单个组件过大(超过 500 行) | JSX 超过 50 行即拆分,文件不超过 300 行 |
| 忽略错误边界 | 在应用、功能或组件层级添加错误边界 |
| 忽视 TypeScript 严格模式 | 启用 strict: true,修复所有错误 |

## 快速参考

### Hooks

| Hook | 用途 |
|------|------|
| useState | 本地状态管理 |
| useEffect | 处理副作用(如订阅、DOM 操作) |
| useCallback | 保持函数引用稳定 |
| useMemo | 处理开销较大的计算 |
| useRef | 可变引用,用于访问 DOM |
| use() | 读取 Promise 或上下文(React 19) |
| useActionState | 表单动作状态(React 19) |
| useOptimistic | 实现乐观 UI(React 19) |

### 文件结构

src/

├── app/ # 路由(Next.js)

├── features/ # 功能模块

│ └── auth/

│ ├── components/ # 功能组件

│ ├── hooks/ # 功能 Hook

│ ├── api/ # API 调用

│ └── index.ts # 公共导出

├── shared/ # 跨功能共享

│ ├── components/ui/ # Button、Input 等通用组件

│ └── hooks/ # useDebounce 等工具 Hook

└── providers/ # 上下文提供者

## 设置

请参阅 `setup.md` 获取首次配置说明。项目跟踪使用 `memory-template.md`。

## 核心规则

1. **服务器状态 ≠ 客户端状态** — API 数据使用 TanStack Query 管理,UI 状态使用 useState/Zustand。禁止混合。
2. **仅使用命名导出** — 使用 `export function X`,而非 `export default`。支持安全重构。
3. **先聚合,再提取** — 初始将状态放在使用位置附近。仅在需要时提升。
4. **不要用 useEffect 处理派生状态** — 直接计算:`const total = items.reduce(...)`。Effects 仅用于副作用。
5. **始终使用稳定 key** — 使用 `item.id`,不要使用 `index` 作为动态列表的 key。
6. **JSX 最多 50 行** — 超过则拆分为独立组件。单个文件最多 300 行。
7. **启用 TypeScript strict: true** — 禁止使用 `any`,禁止隐式空值。在编译期捕获错误。

## 相关技能

通过 `clawhub install <slug>` 安装(用户确认后):

- **frontend-design-ultimate** — 使用 React + Tailwind 构建完整 UI
- **typescript** — TypeScript 使用模式与严格配置
- **nextjs** — Next.js App Router 与部署
- **testing** — 使用 Testing Library 测试 React 组件

## 反馈

- 若有帮助:`clawhub star react`
- 保持更新:`clawhub sync`
I
@ivangdavila

已收录 16 个 Skill

相关推荐