Skip to main content

react

React patterns - component architecture, state management (Zustand, XState), hook composition, and render performance. Use when building React components, managing state with Zustand, composing hooks, or diagnosing re-render performance issues.

Quellinformationen

Repository
greenpill-dev-guild/coop
Letzte Quellaktivität
5. April 2026 um 06:26
Erkannte Sprache von SKILL.md
Englisch
Sterne
3
Forks
1

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
4 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
react
description
React patterns - component architecture, state management (Zustand, XState), hook composition, and render performance. Use when building React components, managing state with Zustand, composing hooks, or diagnosing re-render performance issues.
version
1.0.0
status
active
packages
["shared","app","extension"]
dependencies
[]
last_updated
2026-03-12
last_verified
2026-03-12
# React Skill Complete React guide: state management, component composition, and performance optimization. --- ## Activation When invoked: - Identify state category (local/global/server/form/flow) before choosing a tool. - Keep hooks in `@coop/shared` only. - Use granular Zustand selectors to limit re-renders. ## Part 1: State Management ### State Categories | Type | Description | Solutions | |------|-------------|-----------| | **Local State** | Component-specific UI | useState, useReducer | | **Global State** | Shared across components | Zustand (Coop default) | | **Local-First Data** | Dexie + Yjs persistence | Dexie live queries, Yjs observeDeep | | **URL State** | Route params, search | React Router | | **Form State** | Input values, validation | React Hook Form + Zod | | **Complex Flows** | Multi-step workflows, state machines | XState (Coop flows/) | **Coop uses:** Zustand + XState + Dexie + Yjs ### Zustand Patterns #### Basic Store ```typescript // packages/shared/src/stores/appStore.ts import { create } from "zustand"; import { devtools, persist } from "zustand/middleware"; interface AppState { user: User | null; theme: "light" | "dark"; setUser: (user: User | null) => void; toggleTheme: () => void; } export const useAppStore = create<AppState>()( devtools( persist( (set) => ({ user: null, theme: "light", setUser: (user) => set({ user }), toggleTheme: () => set((state) => ({ theme: state.theme === "light" ? "dark" : "light", })), }), { name: "app-storage" } ) ) ); ``` #### Granular Selectors (Prevent Re-renders) ```typescript // Bad: Subscribes to entire store, re-renders on any change const state = useAppStore(); // Good: Subscribes only to what's needed const user = useAppStore((state) => state.user); const theme = useAppStore((state) => state.theme); // Better: Export selector hooks export const useUser = () => useAppStore((state) => state.user); export const useTheme = () => useAppStore((state) => state.theme); ``` #### Slice Pattern (Scalable) ```typescript import { StateCreator } from "zustand"; export interface UserSlice { user: User | null; isAuthenticated: boolean; login: (credentials: Credentials) => Promise<void>; logout: () => void; } export const createUserSlice: StateCreator< UserSlice & UiSlice, [], [], UserSlice > = (set, get) => ({ user: null, isAuthenticated: false, login: async (credentials) => { const user = await authApi.login(credentials); set({ user, isAuthenticated: true }); }, logout: () => set({ user: null, isAuthenticated: false }), }); ``` ### Combining Client + Local-First State Use Zustand for UI state (sidebar, modals, preferences) and Dexie live queries for persistent local data (coops, tabs, drafts). Never duplicate Dexie data into Zustand -- let Dexie's reactive queries manage data. ### Advanced Zustand Patterns #### Persist Middleware Coop stores use `persist` for state survival across sessions: ```typescript import { create } from "zustand"; import { persist } from "zustand/middleware"; export const useUIStore = create<UIState>()( persist( (set) => ({ debugMode: false, isOfflineBannerVisible: false, toggleDebug: () => set((s) => ({ debugMode: !s.debugMode })), }), { name: "coop:debug-mode", // localStorage key partialize: (state) => ({ debugMode: state.debugMode }), // Only persist specific fields } ) ); ``` **`partialize` is critical** -- without it, transient UI state (modals, banners) gets persisted and restored on refresh, causing stale UI. #### Store Testing ```typescript beforeEach(() => { act(() => useCreateCoopStore.getState().reset()); // Reset between tests }); test("advances step when valid", () => { act(() => { useCreateCoopStore.getState().setField("name", "My Coop"); useCreateCoopStore.getState().nextStep(); }); expect(useCreateCoopStore.getState().currentStep).toBe(1); }); ``` #### When to Use Which Middleware | Middleware | Use When | |-----------|----------| | `persist` | State must survive page refresh (drafts, preferences) | | `devtools` | Development debugging (Redux DevTools integration) | | `immer` | Deep nested state updates (avoid in Coop -- prefer flat state) | ### Hook Boundary Rule **All state hooks MUST be in `@coop/shared`:** ```typescript // Correct: Import from shared import { useAuth, useCoops, useUIStore } from "@coop/shared"; // Wrong: Define hooks in app/extension export function useLocalState() { /* ... */ } ``` --- ## Part 2: Component Composition ### Core Principle > **Composition over configuration** -- Instead of adding props to customize behavior, compose smaller specialized components. ### Avoid Boolean Prop Explosion ```typescript // Bad: Boolean prop explosion <Button primary large withIcon loading disabled outlined /> // Good: Composition + explicit variants <Button variant="primary" size="lg"> <Button.Icon name="save" /> <Button.Text>Save</Button.Text> </Button> ``` ### Compound Components ```typescript // Good: Compound component pattern <Card> <Card.Header> <Card.Title>Coop Details</Card.Title> </Card.Header> <Card.Content> {/* Content */} </Card.Content> <Card.Footer> <Card.Actions> <Button>Edit</Button> </Card.Actions> </Card.Footer> </Card> ``` ### State Encapsulation in Providers ```typescript // Bad: State leaked to consumers function ParentComponent() { const [items, setItems] = useState([]); const [filter, setFilter] = useState(""); return <ItemList items={items} setItems={setItems} filter={filter} />; } // Good: State encapsulated in provider function ItemListProvider({ children }) { const [items, setItems] = useState([]); const [filter, setFilter] = useState(""); const filteredItems = useMemo( () => items.filter(i => i.name.includes(filter)), [items, filter] ); return ( <ItemListContext value={{ items: filteredItems, setFilter }}> {children} </ItemListContext> ); } ``` ### Explicit Variants Over Boolean Modes ```typescript // Bad: Boolean mode <Input error={true} /> <Input success={true} /> // Good: Explicit variant <Input variant="error" /> <Input variant="success" /> ``` ### React 19 Patterns Coop runs **React 19**. Use these new APIs: #### `use()` for Context and Promises ```typescript import { Suspense, use, useMemo } from "react"; // Replace useContext const value = use(MyContext); // Read a promise (suspends until resolved) function CoopDetails({ coopPromise }: { coopPromise: Promise<Coop> }) { const coop = use(coopPromise); // Suspends component return <h1>{coop.name}</h1>; } // Create the promise once per id, outside JSX function CoopDetailsContainer({ id }: { id: string }) { const coopPromise = useMemo(() => fetchCoop(id), [id]); return ( <Suspense fallback={<Skeleton />}> <CoopDetails coopPromise={coopPromise} /> </Suspense> ); } ``` #### `useOptimistic()` for Instant UI Feedback ```typescript import { useOptimistic } from "react"; function TabList({ tabs }: { tabs: Tab[] }) { const [optimisticTabs, addOptimistic] = useOptimistic( tabs, (current, newTab: Tab) => [...current, newTab] ); const handleAdd = async (tab: Tab) => { addOptimistic({ ...tab, status: "pending" }); await saveTab(tab); }; return optimisticTabs.map((t) => <TabCard key={t.id} tab={t} />); } ``` #### When to Use React 19 vs Existing Patterns | Task | React 19 | Existing | |------|----------|----------| | Read context | `use(Context)` | `useContext(Context)` -- both work | | Form submission | `useActionState` | React Hook Form + Zod (preferred for validation) | | Optimistic UI | `useOptimistic` | Manual state + rollback | | Async data | `use(promise)` + Suspense | Dexie live queries (preferred for local-first) | | Ref cleanup | Return cleanup from ref callback | `useEffect` cleanup | **Note:** For Coop, Dexie live queries remain the primary data source (local-first, reactive). Use `use()` for simple one-shot reads where Dexie is overkill. --- ## Part 3: React Compiler & Memoization > For full React Compiler integration details, see [compiler.md](./compiler.md) Coop uses `babel-plugin-react-compiler` in Vite configs. The compiler **automatically memoizes** components, hooks, and expressions -- eliminating most manual `useMemo`, `useCallback`, and `React.memo` usage. **Key rules:** - Don't add `useMemo`, `useCallback`, or `React.memo` by default -- the compiler handles it - Manual memoization still needed for: values passed to non-React APIs, shared hooks consumed across apps - Hooks in `@coop/shared` may still benefit from manual memoization (compiler optimizes per-app build) - When in doubt, profile first with React DevTools Profiler --- ## Part 4: Performance Optimization > For detailed performance patterns and bundle budgets, see [performance.md](./performance.md) **Critical rules:** - Eliminate request waterfalls -- use `Promise.all` for independent fetches - Use direct imports from large libraries (e.g., `import format from "date-fns/format"`) - Dynamic import for below-fold content (`lazy(() => import(...))`) - Use granular Zustand selectors, never subscribe to entire store - Virtualize long lists (50+ items) with @tanstack/react-virtual - CSS animations only (`transform`, `opacity`) **Bundle Budgets:** Main < 150KB, per-route < 50KB, total JS < 400KB (gzipped) --- ## Quick Reference **Before committing:** No serial awaits (use `Promise.all`), dynamic imports for below-fold, direct imports from large libs, long lists virtualized (50+ items), CSS-only animations. **Before designing a component:** Compose from smaller pieces, use explicit variants (not boolean props), use providers to avoid prop drilling, follow existing Coop patterns. **State management:** Zustand for UI state, Dexie for local-first data, granular selectors only, all hooks in `@coop/shared`, forms via React Hook Form + Zod. ## Anti-Patterns - Creating hooks in `app` or `extension` instead of `shared` - Selecting full Zustand state (`state => state`) in UI components - Introducing waterfalls when data can be fetched in parallel - Using deep imports from `@coop/shared/*` instead of barrel imports - Memoizing trivial values without measured performance impact ## Sub-Files - **[compiler.md](./compiler.md)** -- React Compiler integration, automatic memoization, when manual memo is still needed - **[performance.md](./performance.md)** -- Waterfall elimination, bundle budgets, re-render optimization, rendering performance
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen