react
npx skills add https://github.com/dsantiagomj/dsmj-ai-toolkit --skill react
Agent 安装分布
Skill 文档
React – Modern Patterns
Server Components, Actions, and concurrent features for modern React applications
When to Use This Skill
Use this skill when:
- Building user interfaces with React (any version, with focus on React 19 features)
- Implementing Server Components in Next.js or React Server Components framework
- Creating interactive client-side components with hooks
- Building forms with Server Actions
- Optimizing React application performance
- Working with concurrent React features (Suspense, Transitions, etc.)
Don’t use this skill when:
- Building static HTML sites without interactivity (use vanilla HTML/CSS)
- Working with other frameworks (Vue, Svelte, Angular – use their respective skills)
- Building server-only APIs (use backend skills like tRPC, Express)
- Implementing routing in Next.js (use the
nextjsskill instead)
Critical Patterns
Pattern 1: Server vs Client Component Boundary
When: Every React component in modern frameworks (Next.js 13+, React 19)
Good:
// app/dashboard/page.tsx - Server Component (default)
export default async function Dashboard() {
// â
Direct data fetching on server
const stats = await db.stats.findMany()
return (
<div>
<h1>Dashboard</h1>
{/* â
Pass data to client component */}
<InteractiveChart data={stats} />
</div>
)
}
// components/InteractiveChart.tsx - Client Component
'use client'
import { useState } from 'react'
export function InteractiveChart({ data }: { data: Stat[] }) {
const [filter, setFilter] = useState('all')
// â
Interactivity only where needed
return <Chart data={data} filter={filter} onFilterChange={setFilter} />
}
Bad:
// â Making everything a client component
'use client'
import { useEffect, useState } from 'react'
export default function Dashboard() {
const [stats, setStats] = useState([])
// â Client-side data fetching (slower, requires API route)
useEffect(() => {
fetch('/api/stats').then(r => r.json()).then(setStats)
}, [])
return <div>{/* ... */}</div>
}
Why: Server Components reduce JavaScript bundle size, enable direct database access, and improve initial page load. Client Components should only be used for interactivity.
Pattern 2: State Colocation and Lifting
When: Managing state across multiple components
Good:
// â
State colocated with usage
function SearchBar() {
const [query, setQuery] = useState('')
// State lives where it's used
return <input value={query} onChange={(e) => setQuery(e.target.value)} />
}
// â
Lift state only when shared
function FilteredList() {
const [filter, setFilter] = useState('')
return (
<>
<SearchInput value={filter} onChange={setFilter} />
<ResultsList filter={filter} />
</>
)
}
Bad:
// â Premature state lifting (props drilling)
function App() {
// â State at top level when only SearchBar needs it
const [query, setQuery] = useState('')
return <Layout><Header><Nav><SearchBar query={query} setQuery={setQuery} /></Nav></Header></Layout>
}
Why: Keep state as close to where it’s used as possible. Only lift state when multiple components truly need to share it. This improves performance and maintainability.
Pattern 3: Effect Dependencies and Cleanup
When: Using useEffect for side effects
Good:
'use client'
import { useEffect, useState } from 'react'
function ChatRoom({ roomId }: { roomId: string }) {
const [messages, setMessages] = useState<Message[]>([])
useEffect(() => {
const socket = new WebSocket(`wss://chat.example.com/${roomId}`)
socket.onmessage = (event) => {
setMessages(prev => [...prev, JSON.parse(event.data)])
}
// â
Cleanup on unmount or dependency change
return () => {
socket.close()
}
}, [roomId]) // â
Correct dependencies
return <MessageList messages={messages} />
}
Bad:
// â Missing cleanup and wrong dependencies
function ChatRoom({ roomId }: { roomId: string }) {
const [messages, setMessages] = useState<Message[]>([])
useEffect(() => {
const socket = new WebSocket(`wss://chat.example.com/${roomId}`)
socket.onmessage = (event) => {
// â Memory leak: old sockets never closed
setMessages(prev => [...prev, JSON.parse(event.data)])
}
// â No cleanup function
}, []) // â Missing roomId dependency - socket won't reconnect
return <MessageList messages={messages} />
}
Why: Missing cleanup causes memory leaks. Incorrect dependencies cause stale closures and bugs. Always clean up subscriptions, timers, and listeners.
Pattern 4: Memoization for Performance
When: Expensive calculations or preventing unnecessary re-renders
Good:
'use client'
import { useMemo, useCallback, memo } from 'react'
// â
Memoize expensive calculations
function DataTable({ data, filter }: Props) {
const filteredData = useMemo(() => {
// Only recalculates when data or filter changes
return data.filter(item => item.name.includes(filter))
}, [data, filter])
// â
Memoize callbacks passed to children
const handleDelete = useCallback((id: string) => {
deleteItem(id)
}, [])
return <Table data={filteredData} onDelete={handleDelete} />
}
// â
Memo for expensive child components
const ExpensiveRow = memo(({ item }: { item: Item }) => {
const processedData = expensiveProcessing(item)
return <tr>{processedData}</tr>
})
Bad:
// â No memoization - expensive calculation on every render
function DataTable({ data, filter }: Props) {
// â Runs on every render, even when data/filter unchanged
const filteredData = data.filter(item => item.name.includes(filter))
// â New function instance on every render
const handleDelete = (id: string) => deleteItem(id)
return <Table data={filteredData} onDelete={handleDelete} />
}
Why: Memoization prevents unnecessary recalculations and re-renders. Critical for performance in data-heavy applications.
Anti-Patterns
â Anti-Pattern 1: Prop Drilling
Don’t do this:
// â Passing props through many levels
function App() {
const [user, setUser] = useState(null)
return <Layout user={user} setUser={setUser} />
}
function Layout({ user, setUser }) {
return <Sidebar user={user} setUser={setUser} />
}
function Sidebar({ user, setUser }) {
return <UserMenu user={user} setUser={setUser} />
}
function UserMenu({ user, setUser }) {
return <div>{user.name}</div>
}
Why it’s bad: Difficult to maintain, all intermediate components must know about props they don’t use, breaks component encapsulation.
Do this instead:
// â
Use Context for global state
import { createContext, useContext, useState } from 'react'
const UserContext = createContext(null)
function App() {
const [user, setUser] = useState(null)
return (
<UserContext.Provider value={{ user, setUser }}>
<Layout />
</UserContext.Provider>
)
}
function UserMenu() {
const { user } = useContext(UserContext)
return <div>{user.name}</div>
}
â Anti-Pattern 2: Large useEffect with Multiple Responsibilities
// â One effect doing too many things
useEffect(() => {
fetch('/api/user').then(setUser)
const socket = new WebSocket('ws://...')
const timer = setInterval(() => refreshData(), 5000)
analytics.track('page_view')
return () => { socket.close(); clearInterval(timer); }
}, []) // What dependencies are needed? Unclear!
// â
Separate effects for separate concerns
useEffect(() => fetch('/api/user').then(setUser), [])
useEffect(() => {
const socket = new WebSocket('ws://...')
return () => socket.close()
}, [handleMessage])
â Anti-Pattern 3: Mutating State Directly
// â Mutating state directly
const addTodo = (text) => {
todos.push({ id: Date.now(), text }) // â Direct mutation
setTodos(todos) // â React won't detect the change
}
// â
Create new state objects
const addTodo = (text) => {
setTodos([...todos, { id: Date.now(), text }]) // â
New array
}
For more anti-patterns and best practices, see references/best-practices.md.
What This Skill Covers
- Server Components (default in React 19)
- Client Components (use client)
- Server Actions for forms and mutations
- Modern Hooks (useState, useEffect, new React 19 hooks)
- Performance optimization patterns
For detailed patterns, hooks reference, and advanced techniques, see references/.
Server vs Client Components
Server Components (Default)
// app/dashboard/page.tsx
// â
Server Component (default, no directive needed)
export default async function Dashboard() {
const data = await fetchData() // Direct data fetching
return <DashboardUI data={data} />
}
Use Server Components for:
- Data fetching
- Direct database access
- Static content rendering
- SEO-critical content
Benefits:
- Zero JavaScript sent to client
- No API layer needed
- Better performance
- Automatic code splitting
Client Components
'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
)
}
Use Client Components for:
- Event handlers (
onClick,onChange, etc.) - React hooks (
useState,useEffect, etc.) - Browser APIs (
window,localStorage, etc.) - Third-party libraries using browser APIs
Server Actions
// app/actions.ts
'use server'
export async function createUser(formData: FormData) {
await db.users.create({ name: formData.get('name') })
revalidatePath('/users')
}
// app/users/page.tsx
import { createUser } from './actions'
export default function UsersPage() {
return (
<form action={createUser}>
<input name="name" required />
<button type="submit">Create User</button>
</form>
)
}
For advanced Server Actions patterns, see references/server-components.md.
Essential Hooks
useState
'use client'
import { useState } from 'react'
export function Form() {
const [value, setValue] = useState('')
// Functional updates
const increment = () => setCount(prev => prev + 1)
return (
<input
value={value}
onChange={(e) => setValue(e.target.value)}
/>
)
}
useEffect
'use client'
import { useEffect, useState } from 'react'
export function Timer() {
const [seconds, setSeconds] = useState(0)
useEffect(() => {
const interval = setInterval(() => {
setSeconds(s => s + 1)
}, 1000)
// Cleanup
return () => clearInterval(interval)
}, []) // Empty deps = run once
return <div>{seconds}s</div>
}
For advanced hooks (useOptimistic, useTransition, custom hooks), see references/hooks.md.
Component Patterns & Performance
For composition patterns, TypeScript props, React.memo, and performance optimization, see:
- references/typescript.md – TypeScript patterns and props
- references/performance.md – Optimization techniques
Quick Reference
// Server Component (default)
export default async function Page() {
const data = await fetchData()
return <div>{data}</div>
}
// Client Component
'use client'
export default function Interactive() {
const [state, setState] = useState(0)
return <button onClick={() => setState(state + 1)}>{state}</button>
}
// Server Action
'use server'
export async function submitForm(formData: FormData) {
await db.create(formData)
revalidatePath('/items')
}
// Hooks
useState(initial) // State management
useEffect(() => {}, [deps]) // Side effects
useMemo(() => value, [deps]) // Memoize values
useCallback(() => {}, [deps]) // Memoize callbacks
useOptimistic(state, updater) // Optimistic UI
useTransition() // Non-blocking updates
Learn More
- Server Components: references/server-components.md – Deep dive, streaming, Suspense
- Hooks Reference: references/hooks.md – Complete hooks API and patterns
- Performance: references/performance.md – Advanced optimization
- Best Practices: references/best-practices.md – Anti-patterns, error handling
- TypeScript: references/typescript.md – Advanced TypeScript patterns
For Next.js-specific routing, layouts, and app directory patterns, reference the nextjs skill.