plugins/languages/javascript/skills/react/SKILL.md
React 19开发规范:Server Components服务端组件、Actions表单处理、use hook数据获取、Next.js 15 App Router。开发React组件、页面路由、状态管理、SSR服务端渲染时加载。
npx skillsauth add lazygophers/ccplugin plugins/languages/javascript/skills/reactInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| Agent | 说明 | | ----- | ---- | | dev | JavaScript 开发专家 | | test | JavaScript 测试专家 |
| 场景 | Skill | 说明 | |------|-------|------| | 核心规范 | Skills(javascript:core) | ES2025-2026 标准、ESM、工具链 | | 异步编程 | Skills(javascript:async) | async/await、Promise | | 安全编码 | Skills(javascript:security) | XSS 防护、Zod 验证 |
React 19 Compiler 自动记忆化组件和 hooks,手动 useMemo/useCallback/React.memo 现已可选:
// ❌ React 18 写法(冗余,React 19 Compiler 自动处理)
const expensiveValue = useMemo(() => compute(data), [data]);
const handleClick = useCallback(() => onClick(id), [id]);
export default React.memo(MyComponent);
// ✅ React 19 写法(Compiler 自动优化)
const expensiveValue = compute(data);
const handleClick = () => onClick(id);
export default MyComponent;
// use() hook - 在组件中读取 Promise 和 Context
import { use, Suspense } from 'react';
function UserProfile({ userPromise }) {
const user = use(userPromise); // 自动 suspend
return <div>{user.name}</div>;
}
// 用法
<Suspense fallback={<Loading />}>
<UserProfile userPromise={fetchUser(id)} />
</Suspense>
// use() 读取 Context(可在条件语句中使用)
function Theme({ isDark }) {
if (isDark) {
const theme = use(ThemeContext);
return <div style={{ color: theme.text }}>Dark mode</div>;
}
return <div>Light mode</div>;
}
// app/users/page.jsx - Server Component(默认)
export default async function UsersPage() {
const users = await fetch('https://api.example.com/users').then(r => r.json());
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
// app/users/search.jsx - Client Component
'use client';
import { useState } from 'react';
export function UserSearch({ onSearch }) {
const [query, setQuery] = useState('');
return (
<input
value={query}
onChange={e => setQuery(e.target.value)}
onKeyDown={e => e.key === 'Enter' && onSearch(query)}
/>
);
}
// Server Action(Next.js 15)
'use server';
import { z } from 'zod';
const CreateUserSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
});
export async function createUser(prevState, formData) {
const result = CreateUserSchema.safeParse({
name: formData.get('name'),
email: formData.get('email'),
});
if (!result.success) {
return { errors: result.error.flatten().fieldErrors };
}
await db.users.create({ data: result.data });
return { success: true };
}
// Client Component 使用 Action
'use client';
import { useActionState } from 'react';
import { createUser } from './actions.js';
export function CreateUserForm() {
const [state, formAction, isPending] = useActionState(createUser, null);
return (
<form action={formAction}>
<input name="name" required />
{state?.errors?.name && <span>{state.errors.name}</span>}
<input name="email" type="email" required />
{state?.errors?.email && <span>{state.errors.email}</span>}
<button type="submit" disabled={isPending}>
{isPending ? 'Creating...' : 'Create'}
</button>
</form>
);
}
// 自定义 Hook 封装数据获取
export function useUser(userId) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
fetch(`/api/users/${userId}`, { signal: controller.signal })
.then(r => r.json())
.then(setUser)
.catch(e => {
if (e.name !== 'AbortError') setError(e);
})
.finally(() => setLoading(false));
return () => controller.abort();
}, [userId]);
return { user, loading, error };
}
import { memo, useMemo, useCallback, lazy, Suspense } from 'react';
// memo 避免不必要渲染
const UserCard = memo(function UserCard({ user }) {
return <div>{user.name}</div>;
});
// useMemo 缓存计算结果
const sortedUsers = useMemo(
() => users.toSorted((a, b) => a.name.localeCompare(b.name)),
[users]
);
// useCallback 缓存函数引用
const handleClick = useCallback((id) => {
onSelect(id);
}, [onSelect]);
// 路由级代码分割
const Dashboard = lazy(() => import('./pages/Dashboard.jsx'));
<Suspense fallback={<Loading />}>
<Dashboard />
</Suspense>
| 现象 | 问题 | 严重程度 |
|------|------|---------|
| class 组件 | 应使用函数组件 + Hooks | 高 |
| useEffect 获取数据 | React 19 应考虑 use() 或 Server Components | 中 |
| React.FC 定义组件 | 应使用普通函数组件 | 低 |
| 无 key 属性 | 列表渲染必须有稳定的 key | 高 |
| 依赖数组缺失 | useEffect/useMemo/useCallback 依赖不完整 | 高 |
| 过度使用 memo | 简单组件不需要 memo | 低 |
| dangerouslySetInnerHTML 无清理 | 必须使用 DOMPurify | 高 |
use() 替代 useEffect 数据获取useActionState 处理表单提交tools
UI/UX 与布局设计——做界面布局/结构/导航/组件/交互的设计决策。触发:做UI/UX/布局/排版/导航/组件/交互/栅格/响应式/图表选型/字体配对。按媒介路由 HTML/Web、原生 App(iOS/Android/桌面)、CLI、TUI。需后端动态系统不适用;配色/主题/色板走姊妹 skill design-color。
tools
主题与配色设计——做颜色搭配/调色板/主题/品牌色阶/暗模式的设计决策。触发:选配色/调色/主题/色板/品牌色/暗模式/对比度/色盲/UI风格。按媒介路由 HTML/Web(CSS变量)、原生App(平台token)、CLI(ANSI)、TUI(真彩/256/16降级)。保证可访问性(对比度/色盲安全)。需后端动态系统不适用;UI/UX 布局/组件/交互走姊妹 skill design-uiux。
tools
跨任意组件(plugin/skill/agent/command)的验证驱动优化循环纪律 skill。当用户要优化某个已有组件却无明确方向、或要防止改了反而更差(自评乐观偏差 / 多维同改归因失效 / 为凑分加废话膨胀)、或要把一套通用「评分→单变量改→改后验证严格更好才留否则回滚→触顶停」的纪律套到任意组件上时使用。管优化过程本身的纪律(validation gate / ratchet / 独立验证 / 触顶停),不评单组件深度(交 skill-dev),不查插件接线(交 plugin-dev)。仅手动 /optimize-any 触发。
data-ai
两层规则记忆 (基于 .skein/spec)。planning 时 recall 召回相关规则、task finish 后 sediment 沉淀学习 + prune 自动精简过期/重复/断链规则。core 常驻硬规 + recall 按需召回, 经判定门自动写盘 (不逐次问用户)。产出 .skein/spec 下 core/recall 规则文件 + index。另支持空仓 bootstrap 播种规则基线、记忆大面积失效 (大重构/换栈) 时 reconstruct 可逆归档后按项目类型分型重建、maintain 手动体检 (超预算/stale/断链/重复/废弃, --apply 自动修复)、auto-fix (Stop hook 写 .pending-fix 标记 → main 派 skein-specer bg 跑 maintain --apply 全自动修, 断链只报告)。