git clone https://github.com/vibeforge1111/vibeship-spawner-skills
frameworks/react-patterns/skill.yamlid: react-patterns name: React Patterns version: 1.0.0 layer: 1 description: Expert knowledge for React hooks, composition, and component patterns
owns:
- react-hooks
- react-composition
- react-state
- react-context
- react-performance
pairs_with:
- nextjs-app-router
- typescript-strict
- tailwind-ui
requires: []
tags:
- react
- hooks
- components
- state
- context
- performance
- composition
triggers:
- react hook
- useEffect
- useState
- useCallback
- useMemo
- context
- component composition
- react performance
- re-render
identity: | You are a React patterns expert. You understand hooks deeply, know when to use composition vs inheritance, and can optimize performance without premature optimization.
Your core principles:
- Composition over inheritance - build from small, focused components
- Hooks for logic reuse - custom hooks extract and share logic
- Lift state minimally - keep state as close to usage as possible
- Memoize intentionally - profile first, optimize second
- Effects for synchronization - not for derived state
patterns:
-
name: Custom Hook Extraction description: Extract reusable logic into custom hooks when: Same stateful logic used in multiple components example: | // hooks/useLocalStorage.ts function useLocalStorage<T>(key: string, initialValue: T) { const [storedValue, setStoredValue] = useState<T>(() => { if (typeof window === 'undefined') return initialValue try { const item = window.localStorage.getItem(key) return item ? JSON.parse(item) : initialValue } catch { return initialValue } })
const setValue = useCallback((value: T | ((val: T) => T)) => { try { const valueToStore = value instanceof Function ? value(storedValue) : value setStoredValue(valueToStore) window.localStorage.setItem(key, JSON.stringify(valueToStore)) } catch (error) { console.error(error) } }, [key, storedValue]) return [storedValue, setValue] as const}
// Usage const [theme, setTheme] = useLocalStorage('theme', 'light')
-
name: Compound Components description: Related components that share implicit state when: Building complex UI components with multiple parts example: | // Compound component pattern const TabsContext = createContext<TabsContextType | null>(null)
function Tabs({ children, defaultValue }: TabsProps) { const [activeTab, setActiveTab] = useState(defaultValue) return ( <TabsContext.Provider value={{ activeTab, setActiveTab }}> <div className="tabs">{children}</div> </TabsContext.Provider> ) }
function TabList({ children }: { children: React.ReactNode }) { return <div className="tab-list" role="tablist">{children}</div> }
function Tab({ value, children }: TabProps) { const { activeTab, setActiveTab } = useContext(TabsContext)! return ( <button role="tab" aria-selected={activeTab === value} onClick={() => setActiveTab(value)} > {children} </button> ) }
function TabPanel({ value, children }: TabPanelProps) { const { activeTab } = useContext(TabsContext)! if (activeTab !== value) return null return <div role="tabpanel">{children}</div> }
// Attach sub-components Tabs.List = TabList Tabs.Tab = Tab Tabs.Panel = TabPanel
// Usage <Tabs defaultValue="tab1"> <Tabs.List> <Tabs.Tab value="tab1">First</Tabs.Tab> <Tabs.Tab value="tab2">Second</Tabs.Tab> </Tabs.List> <Tabs.Panel value="tab1">First content</Tabs.Panel> <Tabs.Panel value="tab2">Second content</Tabs.Panel> </Tabs>
-
name: Render Props / Children as Function description: Pass render logic as a prop for flexible composition when: Component needs to share data but caller controls rendering example: | // Render prop pattern interface MouseTrackerProps { children: (position: { x: number; y: number }) => React.ReactNode }
function MouseTracker({ children }: MouseTrackerProps) { const [position, setPosition] = useState({ x: 0, y: 0 })
useEffect(() => { const handleMove = (e: MouseEvent) => { setPosition({ x: e.clientX, y: e.clientY }) } window.addEventListener('mousemove', handleMove) return () => window.removeEventListener('mousemove', handleMove) }, []) return <>{children(position)}</>}
// Usage <MouseTracker> {({ x, y }) => ( <div>Mouse is at ({x}, {y})</div> )} </MouseTracker>
-
name: Controlled vs Uncontrolled description: Support both controlled and uncontrolled usage when: Building reusable form components example: | interface InputProps { value?: string // Controlled defaultValue?: string // Uncontrolled onChange?: (value: string) => void }
function Input({ value, defaultValue, onChange }: InputProps) { // Internal state for uncontrolled mode const [internalValue, setInternalValue] = useState(defaultValue ?? '')
// Use provided value if controlled, internal if not const isControlled = value !== undefined const inputValue = isControlled ? value : internalValue const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { const newValue = e.target.value if (!isControlled) { setInternalValue(newValue) } onChange?.(newValue) } return <input value={inputValue} onChange={handleChange} />}
// Controlled usage const [name, setName] = useState('') <Input value={name} onChange={setName} />
// Uncontrolled usage <Input defaultValue="initial" onChange={console.log} />
-
name: Optimistic Updates description: Update UI immediately, roll back on error when: User actions that call APIs (likes, saves, deletes) example: | function LikeButton({ postId, initialLiked }: LikeButtonProps) { const [liked, setLiked] = useState(initialLiked) const [isPending, startTransition] = useTransition()
const toggleLike = async () => { const previousLiked = liked // Optimistic update setLiked(!liked) try { await fetch(`/api/posts/${postId}/like`, { method: liked ? 'DELETE' : 'POST', }) } catch (error) { // Roll back on error setLiked(previousLiked) toast.error('Failed to update') } } return ( <button onClick={toggleLike} disabled={isPending}> {liked ? '❤️' : '🤍'} </button> )}
-
name: Derived State from Props description: Calculate values from props/state without useEffect when: You need computed values based on other state example: | // WRONG - useEffect for derived state function FilteredList({ items, filter }: Props) { const [filteredItems, setFilteredItems] = useState(items)
useEffect(() => { setFilteredItems(items.filter(item => item.includes(filter))) }, [items, filter]) return <List items={filteredItems} />}
// RIGHT - compute during render function FilteredList({ items, filter }: Props) { const filteredItems = useMemo( () => items.filter(item => item.includes(filter)), [items, filter] )
return <List items={filteredItems} />}
// ALSO RIGHT - for simple calculations, skip useMemo function FilteredList({ items, filter }: Props) { const filteredItems = items.filter(item => item.includes(filter)) return <List items={filteredItems} /> }
anti_patterns:
-
name: useEffect for Derived State description: Using useEffect to compute values from props/state why: Causes extra renders, race conditions, and complexity instead: Calculate during render, use useMemo if expensive
-
name: Prop Drilling description: Passing props through many layers of components why: Makes refactoring hard, components tightly coupled instead: Use Context, composition (children), or state management
-
name: Giant Components description: Components with hundreds of lines and many responsibilities why: Hard to test, maintain, and reuse instead: Extract smaller components, custom hooks for logic
-
name: Premature Memoization description: Using useMemo/useCallback everywhere "just in case" why: Adds complexity, memory overhead, often slower than re-computing instead: Profile first, memoize only proven bottlenecks
-
name: State for Everything description: Using useState for values that could be derived or refs why: Unnecessary re-renders, complex state synchronization instead: Derive values, use useRef for non-rendering values
handoffs:
-
trigger: Next.js|server component|client component|app router|server action to: nextjs-app-router priority: 1 context_template: "React patterns in Next.js context: {user_goal}"
-
trigger: TypeScript|type error|generics|interface|type safety to: typescript-strict priority: 1 context_template: "React component needs TypeScript help: {user_goal}"
-
trigger: styling|Tailwind|CSS|design|responsive|dark mode to: tailwind-ui priority: 2 context_template: "React component needs styling: {user_goal}"
-
trigger: state management|Redux|Zustand|Jotai|global state to: state-management priority: 2 context_template: "React app needs state management solution: {user_goal}"
-
trigger: testing|Jest|React Testing Library|unit test|integration test to: qa-engineering priority: 3 context_template: "React component needs tests: {user_goal}"
-
trigger: performance|memo|profiler|bundle size|lazy loading to: frontend priority: 3 context_template: "React app needs performance optimization: {user_goal}"