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
--- name: trellisx-workspace description: 维护 `.trellis/task.md` 任务看板 —— trellis 缺的跨任务总览。**一个表格, 一行一个任务**, 列为 id/名称/描述/状态/阶段/进度/worktree (状态/阶段中文显示)。在 task create/start/阶段切换/archive 后**及时更新**对应行; 并**自动清理超 7 天的已完成行**防膨胀。保持看板与 task.json 实时一致。 when_to_use: 维护 / 创建 / 更新 `.trellis/task.md` 任务看板时; task 生命周期任一节点 (create/start/阶段推进/archive) 之后同步看板时; 用户问"当前有哪些任务 / 任务进度 / 任务看板"时。被 trellisx-flow 与 trellisx-apply 注入的流程引用。 user-invocable: true argument-hint: [show|update|sync|cleanup ...] [task id] arguments:
testing
强制以 Trellis task 闭环处理用户指定的请求 (自判新建/并入 → plan→exec→check→finish 全程不跳步)。**仅用户显式主动调用** (/trellisx-flow 或明确要求"强制走 task 处理这个"); **禁止自动 / 被动 / 推断式调用** —— 不要因为某个请求"看起来该建 task"就自动触发本 skill, 那是 apply 注入的 no_task 倾向的职责。
testing
把 强推task + subtask拆分 + worktree隔离 + 闭环收尾 四维度增量注入当前项目 .trellis/ (workflow.md 的 no_task/planning/in_progress 块 + spec 背书文档 + trellis 生命周期 hook worktree 自动化)。强推 task 与闭环为纯 prompt 软约束 (非平台 hook 硬拦截)。**纯增量追加, 绝不替换 trellis 原生文本** (no_task 分类+征同意/check/finish/前缀全保留)。幂等 (marker 包裹)。
development
Claude Code 会话历史整理 — 扫 ~/.claude/projects/**/*.jsonl 全部 session transcripts, 提取学习增量 (用户校正/决策/踩坑/L0 规则) → 全局记忆库 ~/.cortex/.wiki/memory/. 默认 --apply 落盘 (--dry-run opt-in 仅出 JSON plan 预览). 与 cortex-extract (L4-inbox 内部) 互补.