Skip to main content

react-advanced-patterns

Implements advanced React patterns including render props, higher-order components, custom hooks composition, portals, error boundaries, and ref forwarding for complex component architectures.

설치로 이동

소스 정보

저장소
paulpas/agent-skill-router
최근 소스 활동
2026년 6월 9일 16:28
감지된 SKILL.md 언어
영어
스타
4
포크
1

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
react-advanced-patterns
description
Implements advanced React patterns including render props, higher-order components, custom hooks composition, portals, error boundaries, and ref forwarding for complex component architectures.
license
MIT
compatibility
opencode
archetypes
["tactical","generation"]
anti_triggers
["brainstorming","vague ideation","long-form architecture"]
response_profile
{"verbosity":"low","directive_strength":"high","abstraction_level":"operational"}
metadata
{"version":"1.0.0","domain":"coding","triggers":"react advanced, custom hooks, error boundary, react portal, hoc, forward ref, render props","role":"implementation","scope":"implementation","output-format":"code","content-types":["code","guidance","do-dont","examples"],"related-skills":"react-composition-patterns, react-rerender-optimization, react-client-data-fetching","author":"https://github.com/vercel-labs","source":"https://github.com/vercel-labs/agent-skills"}
# Advanced React Patterns Implements advanced React patterns for cross-cutting concerns: custom hooks for stateful logic reuse, higher-order components for configuration injection, render props for flexible rendering, portals for out-of-DOM rendering, error boundaries for graceful failure, and ref forwarding for imperative access. ## TL;DR Checklist - [ ] Choose hooks over HOCs for stateful logic reuse - [ ] Compose small hooks into larger ones following the single-responsibility principle - [ ] Use createPortal for modals, tooltips, and dropdowns outside parent DOM hierarchy - [ ] Implement error boundaries at route and feature boundaries - [ ] Set displayName on all HOCs for debugging - [ ] Use forwardRef when wrapping native DOM elements - [ ] Test each pattern in isolation before composing --- ## When to Use Use this skill when: - Extracting reusable stateful logic shared across multiple components - Building modals, tooltips, or dropdowns that need to escape overflow/scroll containers - Adding error catch-all boundaries to feature sections or routes - Wrapping third-party DOM elements that need ref access through component layers - Implementing cross-cutting concerns (logging, analytics, authentication) across many components - Creating flexible component APIs where the consumer controls rendering - Exposing imperative methods (scrollTo, focus, reset) on a component via refs --- ## When NOT to Use Avoid this skill for: - Simple state management — useState and useEffect suffice - One-off UI patterns that don't need reuse — premature abstraction - Async data fetching without shared logic — use `react-client-data-fetching` instead - Components that never expose refs or need portal rendering - Situations where custom hooks already solve the problem — don't add HOC complexity --- ## Core Workflow 1. **Identify the Cross-Cutting Concern** — Is it state logic, configuration injection, flexible rendering, or DOM escape? Each maps to a different pattern. 2. **Select the Appropriate Pattern**: - Stateful logic → custom hooks - Configuration/behavior injection → HOC - Flexible render control → render props - Escape parent container → portals (createPortal) - Catch rendering errors → error boundaries - Expose imperative methods → forwardRef + useImperativeHandle 3. **Implement with TypeScript** — Use proper generic types for maximum reusability. 4. **Test in Isolation** — Verify each pattern works independently before composing multiple patterns together. --- ## Implementation Patterns ### Pattern 1: Custom Hooks Composition ```typescript // ✅ GOOD: Small, composable hooks — each has one responsibility function useLocalStorage<T>(key: string, initialValue: T) { const [storedValue, setStoredValue] = useState<T>(() => { try { const item = window.localStorage.getItem(key); return item ? (JSON.parse(item) as T) : initialValue; } catch { return initialValue; } }); const setValue = useCallback( (value: T | ((prev: T) => T)) => { setStoredValue((prev) => { const valueToStore = value instanceof Function ? value(prev) : value; window.localStorage.setItem(key, JSON.stringify(valueToStore)); return valueToStore; }); }, [key] ); return [storedValue, setValue] as const; } function useMediaQuery(query: string): boolean { const [matches, setMatches] = useState(() => matchMedia(query).matches); useEffect(() => { const mql = matchMedia(query); const handler = (e: MediaQueryListEvent) => setMatches(e.matches); mql.addEventListener('change', handler); return () => mql.removeEventListener('change', handler); }, [query]); return matches; } // Compose them for a complex behavior function useResponsiveLocalStorage<T>(key: string, initialValue: T) { const [value, setValue] = useLocalStorage(key, initialValue); const isMobile = useMediaQuery('(max-width: 768px)'); const adjustedValue = useMemo( () => (isMobile ? truncateForMobile(value) : value), [value, isMobile] ); return [adjustedValue, setValue] as const; } ``` ### Pattern 2: Error Boundary with Granular Fallback (BAD vs. GOOD) ```typescript // ❌ BAD: Class component error boundary — works but verbose and lacks TypeScript generics class ErrorBoundary extends React.Component { state = { hasError: false, error: null }; static getDerivedStateFromError(error: Error) { return { hasError: true, error }; } render() { if (this.state.hasError) return <h1>Something went wrong</h1>; return this.props.children; } } ``` ```typescript // ✅ GOOD: Error boundary with typed fallback component and granular control interface ErrorBoundaryProps { children: React.ReactNode; fallback?: React.ComponentType<{ error: Error; reset: () => void }>; onError?: (error: Error, errorInfo: React.ErrorInfo) => void; } interface ErrorBoundaryState { hasError: boolean; error: Error | null; } class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> { constructor(props: ErrorBoundaryProps) { super(props); this.state = { hasError: false, error: null }; } static getDerivedStateFromError(error: Error): ErrorBoundaryState { return { hasError: true, error }; } componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void { this.props.onError?.(error, errorInfo); } handleReset = () => { this.setState({ hasError: false, error: null }); }; render(): React.ReactNode { if (this.state.hasError && this.state.error) { const Fallback = this.props.fallback; if (Fallback) { return <Fallback error={this.state.error} reset={this.handleReset} />; } return <h1>Something went wrong.</h1>; } return this.props.children; } } // Usage: wrap routes or feature sections <ErrorBoundary fallback={({ error, reset }) => ( <div role="alert"> <h2>Section failed to load</h2> <pre>{error.message}</pre> <button onClick={reset}>Retry</button> </div> )} onError={(err) => logErrorToService(err)} > <UserProfile userId={id} /> </ErrorBoundary> ``` ### Pattern 3: Portal for Modal Rendering ```typescript // ✅ GOOD: Portal-based modal escapes parent overflow/stacking context import { createPortal } from 'react-dom'; interface PortalProps { children: React.ReactNode; container?: Element | DocumentFragment; } function Portal({ children, container = document.body }: PortalProps) { return createPortal(children, container); } function Modal({ isOpen, onClose, children }: ModalProps) { useEffect(() => { const handleEscape = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; if (isOpen) { document.addEventListener('keydown', handleEscape); document.body.style.overflow = 'hidden'; } return () => { document.removeEventListener('keydown', handleEscape); document.body.style.overflow = ''; }; }, [isOpen, onClose]); if (!isOpen) return null; return createPortal( <div className="modal-overlay" onClick={onClose}> <div className="modal-content" onClick={(e) => e.stopPropagation()}> {children} </div> </div>, document.body ); } ``` ### Pattern 4: forwardRef + useImperativeHandle ```typescript // ✅ GOOD: Expose imperative methods through forwarded ref interface FancyInputHandle { focus: () => void; reset: () => void; select: () => void; } interface FancyInputProps { defaultValue?: string; onValueChange?: (value: string) => void; } const FancyInput = forwardRef<FancyInputHandle, FancyInputProps>( function FancyInput({ defaultValue, onValueChange }, ref) { const inputRef = useRef<HTMLInputElement>(null); useImperativeHandle( ref, () => ({ focus: () => inputRef.current?.focus(), reset: () => { if (inputRef.current) { inputRef.current.value = defaultValue ?? ''; onValueChange?.(defaultValue ?? ''); } }, select: () => inputRef.current?.select(), }), [defaultValue, onValueChange] ); return ( <input ref={inputRef} defaultValue={defaultValue} onChange={(e) => onValueChange?.(e.target.value)} /> ); } ); // Parent uses imperative handle function Form() { const inputRef = useRef<FancyInputHandle>(null); return ( <> <FancyInput ref={inputRef} defaultValue="hello" /> <button onClick={() => inputRef.current?.reset()}>Reset</button> </> ); } ``` --- ## Constraints ### MUST DO - Use custom hooks over HOCs for stateful logic reuse - Set `displayName` on every HOC for debugging (DevTools) - Use `createPortal` for modals, dialogs, tooltips, and dropdowns - Implement error boundaries at route, feature, or widget boundaries - Use `forwardRef` when wrapping native DOM elements - Expose imperative methods via `useImperativeHandle` when a component needs focus/reset/scroll - Clean up portal DOM nodes and event listeners on unmount ### MUST NOT DO - Over-abstract simple patterns — choose the simplest pattern that works - Use render props or HOCs when custom hooks suffice - Forget to reset error boundary state before retrying - Nest portals unnecessarily — one level of portal is usually sufficient - Ignore the ref type parameter on forwardRef — always type the handle interface - Forget displayName on HOCs — debugging becomes impossible in React DevTools --- ## Related Skills | Skill | Purpose | |---|---| | `react-composition-patterns` | Component composition patterns for building flexible UIs | | `react-rerender-optimization` | Memoization strategies to avoid unnecessary re-renders | | `react-client-data-fetching` | Data fetching patterns with Suspense and error boundaries | | `react-async-waterfalls` | Avoiding request waterfalls in component hierarchies | --- ## Live References > Authoritative documentation links for this skill's domain. The model follows markdown links at load time to resolve external references and inline content. - [React.createPortal API](https://react.dev/reference/react-dom/createPortal) - [React Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) - [React forwardRef API](https://react.dev/reference/react/forwardRef) - [React useImperativeHandle](https://react.dev/reference/react/useImperativeHandle) - [Reusing Logic with Custom Hooks](https://react.dev/learn/reusing-logic-with-custom-hooks) - [Higher-Order Components (Legacy)](https://react.dev/legacy/higher-order-components) - [Render Props Pattern (Legacy)](https://react.dev/legacy/render-props)
GitHub에서 보기