react

📁 dsantiagomj/dsmj-ai-toolkit 📅 12 days ago
4
总安装量
4
周安装量
#48871
全站排名
安装命令
npx skills add https://github.com/dsantiagomj/dsmj-ai-toolkit --skill react

Agent 安装分布

gemini-cli 4
github-copilot 4
codex 4
amp 4
kimi-cli 4
opencode 4

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 nextjs skill 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:


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


For Next.js-specific routing, layouts, and app directory patterns, reference the nextjs skill.