react-patterns

Full Reference: See advanced.md for Container/Presentational, State Machine, Slots, Polymorphic, and Provider patterns.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "react-patterns" with this command: npx skills add claude-dev-suite/claude-dev-suite/claude-dev-suite-claude-dev-suite-react-patterns

React Patterns

Full Reference: See advanced.md for Container/Presentational, State Machine, Slots, Polymorphic, and Provider patterns.

Compound Components

Components that work together to form a cohesive UI:

import { createContext, useContext, useState, ReactNode } from 'react';

interface AccordionContextValue { openItems: Set<string>; toggle: (id: string) => void; }

const AccordionContext = createContext<AccordionContextValue | null>(null);

function useAccordion() { const context = useContext(AccordionContext); if (!context) { throw new Error('Accordion components must be used within Accordion'); } return context; }

function Accordion({ children, multiple = false, defaultOpen = [] }: AccordionProps) { const [openItems, setOpenItems] = useState<Set<string>>(new Set(defaultOpen));

const toggle = (id: string) => { setOpenItems((prev) => { const next = new Set(prev); if (next.has(id)) { next.delete(id); } else { if (!multiple) next.clear(); next.add(id); } return next; }); };

return ( <AccordionContext.Provider value={{ openItems, toggle }}> <div className="accordion">{children}</div> </AccordionContext.Provider> ); }

// Attach sub-components Accordion.Item = Item; Accordion.Trigger = Trigger; Accordion.Content = Content;

Render Props

Pass a function as children or prop to control rendering:

interface DataFetcherProps<T> { url: string; children: (state: { data: T | null; loading: boolean; error: Error | null; refetch: () => void; }) => ReactNode; }

function DataFetcher<T>({ url, children }: DataFetcherProps<T>) { const [data, setData] = useState<T | null>(null); const [loading, setLoading] = useState(true); const [error, setError] = useState<Error | null>(null);

const fetchData = useCallback(async () => { setLoading(true); setError(null); try { const res = await fetch(url); if (!res.ok) throw new Error('Failed to fetch'); setData(await res.json()); } catch (e) { setError(e as Error); } finally { setLoading(false); } }, [url]);

useEffect(() => { fetchData(); }, [fetchData]);

return <>{children({ data, loading, error, refetch: fetchData })}</>; }

// Usage <DataFetcher<User[]> url="/api/users"> {({ data, loading, error, refetch }) => { if (loading) return <Spinner />; if (error) return <ErrorMessage error={error} onRetry={refetch} />; return <ul>{data?.map(user => <li key={user.id}>{user.name}</li>)}</ul>; }} </DataFetcher>

Higher-Order Components (HOC)

Wrap components to add functionality:

function withLoading<P extends object>( WrappedComponent: React.ComponentType<P>, LoadingComponent: React.ComponentType = DefaultSpinner ) { return function WithLoadingComponent(props: P & { loading: boolean }) { const { loading, ...restProps } = props; if (loading) return <LoadingComponent />; return <WrappedComponent {...(restProps as P)} />; }; }

// HOC with Refs function withLogger<P extends object>(WrappedComponent: React.ComponentType<P>) { const WithLogger = React.forwardRef<unknown, P>((props, ref) => { useEffect(() => { console.log('Props changed:', props); }, [props]); return <WrappedComponent {...props} ref={ref} />; }); WithLogger.displayName = WithLogger(${WrappedComponent.displayName || WrappedComponent.name}); return WithLogger; }

Custom Hooks Pattern

Extract reusable logic into hooks:

// useLocalStorage function useLocalStorage<T>(key: string, initialValue: T) { const [storedValue, setStoredValue] = useState<T>(() => { try { const item = window.localStorage.getItem(key); return item ? JSON.parse(item) : initialValue; } catch { return initialValue; } });

const setValue = useCallback((value: T | ((val: T) => T)) => { const valueToStore = value instanceof Function ? value(storedValue) : value; setStoredValue(valueToStore); window.localStorage.setItem(key, JSON.stringify(valueToStore)); }, [key, storedValue]);

return [storedValue, setValue] as const; }

// useDebounce function useDebounce<T>(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const timer = setTimeout(() => setDebouncedValue(value), delay); return () => clearTimeout(timer); }, [value, delay]); return debouncedValue; }

// useOnClickOutside function useOnClickOutside( ref: RefObject<HTMLElement>, handler: (event: MouseEvent | TouchEvent) => void ) { useEffect(() => { const listener = (event: MouseEvent | TouchEvent) => { if (!ref.current || ref.current.contains(event.target as Node)) return; handler(event); }; document.addEventListener('mousedown', listener); document.addEventListener('touchstart', listener); return () => { document.removeEventListener('mousedown', listener); document.removeEventListener('touchstart', listener); }; }, [ref, handler]); }

Composition Pattern

Prefer composition over inheritance:

function Card({ children, className }: CardProps) { return <div className={card ${className || ''}}>{children}</div>; }

function CardHeader({ children }: { children: ReactNode }) { return <div className="card-header">{children}</div>; }

function CardBody({ children }: { children: ReactNode }) { return <div className="card-body">{children}</div>; }

// Specialized cards through composition function UserCard({ user }: { user: User }) { return ( <Card> <CardHeader> <Avatar src={user.avatar} /> <h3>{user.name}</h3> </CardHeader> <CardBody><p>{user.bio}</p></CardBody> </Card> ); }

Best Practices Summary

Pattern When to Use

Compound Components Flexible, related components

Render Props Share behavior, control rendering

HOC Cross-cutting concerns, legacy code

Custom Hooks Reusable stateful logic

Composition Building complex from simple

Container/Presentational Separate logic from UI

State Machine Complex state transitions

Polymorphic Flexible element types

  • Use custom hooks for reusable logic

  • Keep components small and focused

  • Use TypeScript for better DX

  • Don't overuse patterns - KISS

  • Don't nest too many HOCs

Anti-Patterns

Anti-Pattern Problem Solution

Over-engineering simple components Unnecessary complexity Use simple props

Nesting too many HOCs Props collision, hard to debug Use composition or hooks

Render props without memoization Performance issues Memoize render function

Deep prop drilling Hard to maintain Use Context or Compound Components

Context for frequently changing values Performance issues Use state library

Quick Troubleshooting

Issue Likely Cause Fix

Context not available Missing Provider Wrap with Provider component

HOC losing refs Not forwarding refs Use React.forwardRef

Render props re-rendering Function identity changes Memoize render function

Reference Documentation

  • React Patterns

  • React TypeScript Cheatsheet

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

react-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

react-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

cron-scheduling

No summary provided by upstream source.

Repository SourceNeeds Review