Przejdź do treści
Biblioteka skilli
Programowanie · Budowanie i kod

react-patterns

/react-patterns

Podpowiada wzorce React 18 i 19: hooki, granice komponentów serwer-klient, Suspense i błędy, akcje formularzy oraz dostępność.

Repozytorium
Nemo-Cli ↗
Licencja
Apache-2.0
Ostatnia zmiana repozytorium
05.11.2026

Ocena i bezpieczeństwo

100/ 100Klasa: dobry
Format25 / 25
Frontmatter z nazwą i opisem, poprawna nazwa, opis do 1024 znaków, nazwa zgodna z folderem.
Odkrywalność25 / 25
Opis mówi, kiedy użyć skilla, ma sensowną długość i jest w 3. osobie.
Treść35 / 35
Co najmniej 150 słów i do 500 linii, nagłówki, listy kroków, przykłady albo kod.
Higiena15 / 15
Bez zaślepek typu TODO, bez ścieżek Windows, z licencją.
Ryzykobrak sygnałów w skanie
Pliki w paczce
1
Skrypty
0
Komentarze HTML
0
Adresy URL w SKILL.md
0
Przestarzałość

Brak sygnałów. Repozytorium zmienione 05.11.2026, bez odwołań do wycofanych modeli i API.

Licencja

Apache-2.0, otwarta: można używać i zmieniać, z zachowaniem licencji i informacji o zmianach.

Co robi i kiedy

Podpowiada wzorce React 18 i 19: hooki, granice komponentów serwer-klient, Suspense i błędy, akcje formularzy oraz dostępność.

Oryginalny opis (EN)

React 18/19 patterns including hooks discipline, server/client component boundaries, Suspense + error boundaries, form actions, data fetching, state management decision trees, and accessibility-first composition. Use when writing or reviewing React components.

Jak działa

  1. When to Activate
  2. Core Principles
  3. Hooks Discipline
  4. State Location Decision Tree
  5. Server / Client Components (RSC)
  6. Suspense + Error Boundaries
  7. Forms
  8. Data Fetching Decision Matrix
  9. Composition Recipes
  10. Performance
Sekcje z nagłówków SKILL.md, w oryginale · 339 linii, 1 472 słów
Podgląd SKILL.md
---
name: react-patterns
description: React 18/19 patterns including hooks discipline, server/client component boundaries, Suspense + error boundaries, form actions, data fetching, state management decision trees, and accessibility-first composition. Use when writing or reviewing React components.
metadata:
  origin: ECC
---

# React Patterns

Idiomatic React 18/19 patterns for building robust, accessible, performant component trees.

## When to Activate

- Writing or modifying React function components, custom hooks, or component trees
- Reviewing JSX/TSX files
- Designing state shape or component composition
- Migrating class components or older `forwardRef`/`useEffect`-heavy code
- Choosing between local state, lifted state, context, and external stores
- Working with Server Components / Client Components (Next.js App Router, RSC)
- Implementing forms with React 19 actions or controlled inputs
- Wiring data fetching with TanStack Query / SWR / RSC

## Core Principles

### 1. Render is a Pure Function of Props and State

```tsx
// Good: derive during render
function Cart({ items }: { items: CartItem[] }) {
  const total = items.reduce((sum, i) => sum + i.price * i.qty, 0)
  return <span>{formatMoney(total)}</span>
}

// Bad: derived state stored separately
function Cart({ items }: { items: CartItem[] }) {
  const [total, setTotal] = useState(0)
  useEffect(() => {
    setTotal(items.reduce((sum, i) => sum + i.price * i.qty, 0))
  }, [items])
  return <span>{formatMoney(total)}</span>
}
```

Derived state in `useEffect` adds a render cycle, can desync, and obscures the data flow.

### 2. Side Effects Outside Render

Effects, mutations, network calls, and subscriptions live in event handlers or `useEffect` — never in the render body.

### 3. Composition Over Inheritance

React has no inheritance model for components. Compose with `children`, render props, or component props.

## Hooks Discipline

See [rules/react/hooks.md](../../rules/react/hooks.md) for the full ruleset. Highlights:

- Top-level only, never conditional
- Cleanup every subscription, interval, listener
- Functional updater (`setX(prev => prev + 1)`) when new state depends on old
- Default position: do not memoize — add `useMemo`/`useCallback` only when a profiler or a dependency chain proves it matters
- Extract a custom hook only when the same hook sequence appears in 2+ components

## State Location Decision Tree

```
Used by one component?
  -> useState inside it

Used by parent + a few descendants?
  -> lift to nearest common ancestor

Used across distant branches AND low-frequency reads (theme, auth, locale)?
  -> React Context

High-frequency updates shared across the tree?
  -> external store (Zustand, Jotai, Redux Toolkit)

Derived from a server?
  -> server-state library (TanStack Query, SWR, RSC fetch)
```

Most pages do not need context or a global store. Resist abstraction until duplicated lifting becomes painful.

## Server / Client Components (RSC)

```tsx
// Server Component - default, async, never ships JS for itself
export default async function ProductPage({ params }: { params: { id: string } }) {
  const product = await db.product.findUnique({ where: { id: params.id } })
  if (!product) notFound()
  return <ProductView product={product} />
}

// Client Component - opt in with "use client"
;("use client")
export function AddToCartButton({ productId }: { productId: string }) {
  const [pending, startTransition] = useTransition()
  return (
    <button disabled={pending} onClick={() => startTransition(() => addToCart(productId))}>
      {pending ? "Adding..." : "Add to cart"}
    </button>
  )
}
```

Boundaries:

- Server -> Client: pass serializable props or `children`
- Client -> Server: invoke Server Actions via `<form action={...}>` or imperatively from event handlers
- Never `import` a Server Component from a Client Component file — compose them via `children` instead

## Suspense + Error Boundaries

```tsx
<ErrorBoundary fallback={<ErrorView />}>
  <Suspense fallback={<UserSkeleton />}>
    <UserDetail id={id} />
  </Suspense>
</ErrorBoundary>
```

- Place Suspense boundaries close to the data, not at the route root — progressively reveal content
- Error Boundary remains a class API; use `react-error-boundary` for a hook-friendly wrapper
- A boundary catches errors thrown during render, lifecycle, and constructors of its children — NOT in event handlers or async code

## Forms

### React 19 form actions (preferred for new code)

```tsx
"use client"
import { useActionState } from "react"

const initial = { error: null as string | null }

async function updateUserAction(_prev: typeof initial, formData: FormData) {
  "use server"
  const parsed = UserSchema.safeParse(Object.fromEntries(formData))
  if (!parsed.success) return { error: "Invalid input" }
  await db.user.update({ where: { id: parsed.data.id }, data: parsed.data })
  return { error: null }
}

export function UserForm() {
  const [state, formAction, pending] = useActionState(updateUserAction, initial)
  return (
    <form action={formAction}>
      <input name="name" required />
      <button type="submit" disabled={pending}>
        Save
      </button>
      {state.error && <p role="alert">{state.error}</p>}
    </form>
  )
}
```

### Controlled inputs

Use controlled when the value drives other UI, formats on every keystroke, or implements real-time validation.

### Complex forms

For multi-step forms, dynamic field arrays, or cross-field validation: use a library (React Hook Form, TanStack Form). Roll-your-own state management for forms past trivial complexity is a maintenance trap.

## Data Fetching Decision Matrix

| Need                                         | Tool                                                          |
| -------------------------------------------- | ------------------------------------------------------------- |
| Per-request data in Next.js App Router       | RSC `await fetch()`                                           |
| Client-side cache + mutations + invalidation | TanStack Query                                                |
| Lightweight client cache + revalidation      | SWR                                                           |
| Real-time subscriptions                      | Server-Sent Events, WebSockets, or the lib's subscription API |
| One-off fire-and-forget                      | `fetch()` in an event handler                                 |

Avoid `useEffect` + `fetch` for application data — race conditions, no cache, no retry, no Suspense integration.

## Composition Recipes

### Slot via `children`

```tsx
<Layout>
  <Header />
  <Main>{content}</Main>
</Layout>
```

### Named slots

```tsx
<Page header={<Nav />} sidebar={<Filters />}>
  <Results />
</Page>
```

### Compound components (shared state via Context)

```tsx
<Tabs defaultValue="profile">
  <Tabs.List>
    <Tabs.Trigger value="profile">Profile</Tabs.Trigger>
    <Tabs.Trigger value="settings">Settings</Tabs.Trigger>
  </Tabs.List>
  <Tabs.Panel value="profile">
    <Profile />
  </Tabs.Panel>
  <Tabs.Panel value="settings">
    <Settings />
  </Tabs.Panel>
</Tabs>
```

### Render prop / function-as-child

Useful when the parent needs to pass parameters to the rendered output:

```tsx
<DataLoader id={id}>{({ data, isLoading }) => (isLoading ? <Spinner /> : <UserCard user={data} />)}</DataLoader>
```

Modern alternative: a hook (`useData(id)`) returning the same shape — usually cleaner.

## Performance

### When `React.memo` Actually Helps

Wrap a component in `React.memo` only when:

1. It re-renders frequently
2. Its props are usually the same between renders
3. Its render is measurably expensive

`React.memo` adds an equality check on every render. If props differ on most renders, the check is pure overhead.

### Avoiding Render Cascades

- Lift state down rather than up where possible
- Split context: one context per concern, so a change to `themeContext` does not re-render auth consumers
- Use `useSyncExternalStore` for external state libraries — required for safe concurrent rendering

### Lists

- Provide stable `key` props (database id, not array index)
- Virtualize long lists with `@tanstack/react-virtual` or `react-window` once visible item count exceeds ~50 with non-trivial rows

## Accessibility-First Composition

- Always render semantic HTML (`<button>`, `<a>`, `<nav>`, `<main>`) before reaching for `role` attributes
- Every interactive element must be reachable by keyboard
- Form inputs need labels — `<label htmlFor>` or `aria-label` if visually labeled by an icon
- Manage focus on route changes and modal open/close
- Run `axe` in component tests (see [skills/react-testing](../react-testing/SKILL.md))
- Cross-link: [skills/accessibility/SKILL.md](../accessibility/SKILL.md) covers WCAG criteria and pattern libraries

## Routing

This skill is router-agnostic. The patterns above work with React Router, TanStack Router, Next.js App Router, Remix Router. Router-specific patterns (loaders, actions, nested layouts) follow the router's documentation — those are framework concerns layered on top of React core.

## Out of Scope (Pointer Sections)

- **Next.js specifics**: App Router data loading, Route Handlers, Middleware, Parallel Routes — separate concern, use Next.js docs
- **React Native**: Platform-specific patterns differ enough to warrant a separate `react-native-patterns` skill (not present yet)
- **Remix**: Loader/action conventions overlap with RSC but follow Remix docs

## Related

- Rules: [rules/react/](../../rules/react/) — coding-style, hooks, patterns, security, testing
- Skills: [react-performance](../react-performance/SKILL.md) for the Vercel-derived performance ruleset, [frontend-patterns](../frontend-patterns/SKILL.md) for cross-framework UI concerns, [accessibility](../accessibility/SKILL.md), [angular-developer](../angular-developer/SKILL.md) for framework comparison
- Agents: `react-reviewer` for code review, `react-build-resolver` for build/bundler errors
- Commands: `/react-review`, `/react-build`, `/react-test`

## Examples

### Custom hook for debounced search

```tsx
function useDebounce<T>(value: T, delay = 300): T {
  const [debounced, setDebounced] = useState(value)
  useEffect(() => {
    const id = setTimeout(() => setDebounced(value), delay)
    return () => clearTimeout(id)
  }, [value, delay])
  return debounced
}

function SearchBox() {
  const [query, setQuery] = useState("")
  const debounced = useDebounce(query, 300)
  const { data } = useQuery({
    queryKey: ["search", debounced],
    queryFn: () => searchApi(debounced),
    enabled: debounced.length > 0,
  })
  return (
    <>
      <input value={query} onChange={(e) => setQuery(e.target.value)} />
      <Results items={data ?? []} />
    </>
  )
}
```

### Optimistic UI with React 19 `useOptimistic`

```tsx
"use client"
import { useOptimistic } from "react"

export function MessageList({ messages }: { messages: Message[] }) {
  const [optimistic, addOptimistic] = useOptimistic(messages, (state, newMessage: Message) => [...state, newMessage])

  async function send(formData: FormData) {
    const text = String(formData.get("text"))
    addOptimistic({ id: "pending", text, sender: "me" })
    await saveMessage(text)
  }

  return (
    <>
      <ul>
        {optimistic.map((m) => (
          <li key={m.id}>{m.text}</li>
        ))}
      </ul>
      <form action={send}>
        <input name="text" />
        <button type="submit">Send</button>
      </form>
    </>
  )
}
```

### Splitting context to avoid render cascades

```tsx
// Two contexts: one rarely changes, one frequently
const ThemeContext = createContext<Theme>("light")
const NotificationsContext = createContext<Notification[]>([])

// A component that only consumes ThemeContext does NOT re-render when notifications change
```

Wersje

RepozytoriumWynikLicencja
Ta strona · SHRIJANAGAIN/Nemo-Cli100Apache-2.0
ahmet-cetinkaya/dotfiles100MIT
Sanit-mann/The-Insectoids100MIT
komluk/scaffolding100MIT
affaan-m/ECC100MIT
nokri-mohammed/claude-team100MIT
claude-dev-suite/claude-dev-suite100MIT
reddygopalkrishna/developer-agent-skills100MIT
sagargupta16/claude-code-recipes100MIT

Migawka bazy z 13.11.2026 · wersja paczki f2c7ab9fe1a0 (skrót SHA-256 całej paczki)

Pobrania w ostatnich 30 dniach: 0 · ranking

Zgłoś problem z tym skillem: szkodliwa treść, błędny opis albo prośba autora o usunięcie.

Podobne skille

  • 100react-idioms
    Podpowiada idiomy React 19: własne hooki, granice Suspense, kompozycję komponentów, zarządzanie stanem i wydajność w przeglądarce.
  • 100react-expert
    Projektuje architekturę aplikacji React 19.2+, obejmującą komponenty, hooki, Server Components, granice Suspense i wydajność.
  • 100react
    Obejmuje podstawowe wzorce React 19: hooki, Suspense, lazy loading, strukturę komponentów, dobre praktyki TypeScript i optymalizację wydajności.
  • 100react-ops
    Obejmuje wzorce programowania w React: hooki, zarządzanie stanem, Server Components, optymalizacja wydajności, zustand, react query, error boundary i Suspense w React 19.
  • 95react
    Pomaga budować funkcje React: własne hooki, Suspense, granice błędów, Server Components i animacje.
  • 100react-stinger
    Przegląda i refaktoryzuje architekturę React 18 i 19: warstwy stanu, Server Components, Suspense, Actions, granice danych i testowanie.

Uwagi i zgłoszenia

Wiadomość trafi do Bartosza Sokolińskiego. Adres e-mail jest potrzebny tylko do odpowiedzi. Szczegóły w polityce prywatności.