
React Patterns
- 337 installs
- 61 repo stars
- Updated June 13, 2026
- manutej/luxor-claude-marketplace
react-patterns is a Claude Code skill that applies proven React architectural patterns for composition, state boundaries, rendering performance, and maintainability when frontend complexity grows beyond simple trees.
About
react-patterns is a frontend skill from manutej/luxor-claude-marketplace that teaches agents maintainable React architecture as applications scale. The skill addresses composition strategies, state boundary placement, memoization and rendering performance, and patterns that keep component trees readable when features accumulate. Developers reach for react-patterns when prop drilling, redundant re-renders, or unclear ownership slow feature work—not when bootstrapping a trivial single-page demo. It bridges everyday React code with structural decisions about containers, hooks colocation, and performance guardrails agents can apply during refactors or greenfield feature modules.
- Composition over inheritance
- State colocation strategies
- Render optimization patterns
- Reusable component APIs
- Scalable folder and module structure
React Patterns by the numbers
- 337 all-time installs (skills.sh)
- +18 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #709 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/manutej/luxor-claude-marketplace --skill react-patternsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 337 |
|---|---|
| repo stars | ★ 61 |
| Last updated | June 13, 2026 |
| Repository | manutej/luxor-claude-marketplace ↗ |
How do you structure large React applications?
Apply proven React architectural patterns for composition, state boundaries, rendering performance, and maintainability when frontend complexity grows beyond simple component trees.
Who is it for?
Frontend engineers maintaining medium-to-large React codebases who need architectural guidance beyond basic component syntax.
Skip if: Brand-new create-react-app tutorials, backend API design, or greenfield apps with only a handful of static components.
When should I use this skill?
A React codebase shows prop drilling, unclear state ownership, or re-render issues and needs composition and performance patterns.
What you get
Refactored component hierarchy, colocated state boundaries, and performance patterns documented for ongoing React feature work.
- component architecture refactor plan
- state boundary recommendations
Files
React Patterns - Modern Development Guide
A comprehensive skill for mastering modern React development patterns, including React 18+ features, hooks, component composition, state management strategies, and performance optimization techniques for building scalable web applications.
When to Use This Skill
Use this skill when:
- Building modern React applications with functional components and hooks
- Implementing complex state management with useReducer and Context API
- Optimizing React application performance with memoization techniques
- Creating reusable custom hooks for shared logic
- Working with Server Components and React Server Components (RSC)
- Managing forms, side effects, and asynchronous operations
- Refactoring class components to modern functional patterns
- Building type-safe React applications with proper patterns
- Implementing advanced component composition patterns
- Debugging React performance issues and unnecessary re-renders
Core Concepts
React Philosophy
Modern React emphasizes:
- Functional Components: Pure functions that return JSX
- Hooks: Composable state and side effect management
- Declarative UI: Describe what the UI should look like, React handles updates
- Component Composition: Build complex UIs from simple, reusable components
- Unidirectional Data Flow: Props flow down, events flow up
- Immutability: Never mutate state directly, always create new objects/arrays
Component Types
1. Presentational Components: Focus on UI, receive data via props 2. Container Components: Handle logic, state, and side effects 3. Server Components: Render on the server, no client JavaScript 4. Client Components: Interactive components marked with "use client" 5. Async Components: Server components that can await data
React Hooks Reference
Built-in State Hooks
useState
Manage local component state for simple values.
Syntax:
const [state, setState] = useState(initialValue);Basic Example:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(prev => prev + 1)}>Increment (functional)</button>
</div>
);
}Best Practices:
- Use functional updates when new state depends on previous state
- Keep state as local as possible
- Don't store derived values in state
- Initialize with functions for expensive computations
Multiple State Variables:
function Form() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState(0);
return (
<form>
<input value={name} onChange={e => setName(e.target.value)} />
<input value={email} onChange={e => setEmail(e.target.value)} />
<input type="number" value={age} onChange={e => setAge(Number(e.target.value))} />
</form>
);
}Lazy Initialization:
function ExpensiveComponent() {
// Function only runs once on initial render
const [state, setState] = useState(() => {
const initialValue = expensiveComputation();
return initialValue;
});
return <div>{state}</div>;
}useReducer
Manage complex state logic with a reducer pattern (similar to Redux).
Syntax:
const [state, dispatch] = useReducer(reducer, initialState, init?);Task Manager Example:
import { useReducer } from 'react';
function tasksReducer(tasks, action) {
switch (action.type) {
case 'added':
return [...tasks, {
id: action.id,
text: action.text,
done: false
}];
case 'changed':
return tasks.map(t =>
t.id === action.task.id ? action.task : t
);
case 'deleted':
return tasks.filter(t => t.id !== action.id);
default:
throw Error('Unknown action: ' + action.type);
}
}
function TaskApp() {
const [tasks, dispatch] = useReducer(tasksReducer, []);
function handleAddTask(text) {
dispatch({
type: 'added',
id: Date.now(),
text: text,
});
}
function handleChangeTask(task) {
dispatch({
type: 'changed',
task: task
});
}
function handleDeleteTask(taskId) {
dispatch({
type: 'deleted',
id: taskId
});
}
return (
<>
<AddTask onAddTask={handleAddTask} />
<TaskList
tasks={tasks}
onChangeTask={handleChangeTask}
onDeleteTask={handleDeleteTask}
/>
</>
);
}When to use useReducer:
- Multiple related state values
- Complex state update logic
- State transitions follow predictable patterns
- Need to optimize performance with many updates
- Want to separate state logic from component
useContext
Access context values without prop drilling.
Syntax:
const value = useContext(SomeContext);Theme Context Example:
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext(null);
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// Usage
function Button() {
const { theme, toggleTheme } = useTheme();
return (
<button
onClick={toggleTheme}
className={theme === 'dark' ? 'btn-dark' : 'btn-light'}
>
Toggle Theme
</button>
);
}Combining useReducer + useContext:
import { createContext, useContext, useReducer } from 'react';
const TasksContext = createContext(null);
const TasksDispatchContext = createContext(null);
export function TasksProvider({ children }) {
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
return (
<TasksContext.Provider value={tasks}>
<TasksDispatchContext.Provider value={dispatch}>
{children}
</TasksDispatchContext.Provider>
</TasksContext.Provider>
);
}
export function useTasks() {
return useContext(TasksContext);
}
export function useTasksDispatch() {
return useContext(TasksDispatchContext);
}
// Usage in components
function TaskList() {
const tasks = useTasks();
const dispatch = useTasksDispatch();
return (
<ul>
{tasks.map(task => (
<li key={task.id}>
{task.text}
<button onClick={() => dispatch({ type: 'deleted', id: task.id })}>
Delete
</button>
</li>
))}
</ul>
);
}Effect Hooks
useEffect
Synchronize component with external systems (APIs, DOM, subscriptions).
Syntax:
useEffect(() => {
// Effect logic
return () => {
// Cleanup logic
};
}, [dependencies]);Data Fetching Example:
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let ignore = false; // Prevent race conditions
async function fetchUser() {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
if (!ignore) {
setUser(data);
}
} catch (err) {
if (!ignore) {
setError(err.message);
}
} finally {
if (!ignore) {
setLoading(false);
}
}
}
fetchUser();
return () => {
ignore = true; // Cleanup
};
}, [userId]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>User: {user?.name}</div>;
}Subscription Example:
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const connection = createConnection(roomId);
connection.on('message', (msg) => {
setMessages(prev => [...prev, msg]);
});
connection.connect();
return () => {
connection.disconnect(); // Cleanup on unmount or roomId change
};
}, [roomId]);
return (
<div>
{messages.map((msg, i) => (
<div key={i}>{msg}</div>
))}
</div>
);
}Common Effect Patterns:
// Run once on mount
useEffect(() => {
console.log('Component mounted');
}, []); // Empty dependency array
// Run on every render
useEffect(() => {
console.log('Component rendered');
}); // No dependency array
// Run when specific values change
useEffect(() => {
console.log('userId changed:', userId);
}, [userId]); // Dependency array with values
// Cleanup on unmount
useEffect(() => {
const timer = setTimeout(() => {
console.log('Delayed action');
}, 1000);
return () => clearTimeout(timer);
}, []);useLayoutEffect
Synchronous version of useEffect, runs before browser paint.
Use Cases:
- Measuring DOM elements
- Synchronous DOM mutations
- Preventing visual flickering
Example:
import { useLayoutEffect, useRef, useState } from 'react';
function Tooltip() {
const ref = useRef(null);
const [tooltipHeight, setTooltipHeight] = useState(0);
useLayoutEffect(() => {
const { height } = ref.current.getBoundingClientRect();
setTooltipHeight(height); // Synchronous update before paint
}, []);
return (
<div ref={ref}>
Tooltip content (height: {tooltipHeight}px)
</div>
);
}Performance Hooks
useMemo
Cache expensive computations between renders.
Syntax:
const cachedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);Example:
import { useMemo } from 'react';
function TodoList({ todos, filter }) {
// Only recompute when todos or filter changes
const visibleTodos = useMemo(() => {
console.log('Filtering todos...');
return todos.filter(todo => {
if (filter === 'all') return true;
if (filter === 'active') return !todo.done;
if (filter === 'completed') return todo.done;
return true;
});
}, [todos, filter]);
return (
<ul>
{visibleTodos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}When to use useMemo:
- Expensive calculations (filtering, sorting large arrays)
- Preventing unnecessary re-renders of child components
- Stabilizing object/array references passed as dependencies
Anti-pattern (unnecessary):
// ❌ Don't use for simple calculations
const sum = useMemo(() => a + b, [a, b]); // Overkill
// ✅ Just compute directly
const sum = a + b;useCallback
Memoize function references between renders.
Syntax:
const cachedFn = useCallback(() => {
// Function logic
}, [dependencies]);Example:
import { useState, useCallback, memo } from 'react';
const TodoItem = memo(function TodoItem({ todo, onChange, onDelete }) {
console.log('TodoItem rendered:', todo.id);
return (
<li>
<input
type="checkbox"
checked={todo.done}
onChange={() => onChange(todo)}
/>
{todo.text}
<button onClick={() => onDelete(todo.id)}>Delete</button>
</li>
);
});
function TodoList() {
const [todos, setTodos] = useState([]);
// Memoize handlers to prevent TodoItem re-renders
const handleChange = useCallback((todo) => {
setTodos(prev => prev.map(t =>
t.id === todo.id ? { ...t, done: !t.done } : t
));
}, []);
const handleDelete = useCallback((id) => {
setTodos(prev => prev.filter(t => t.id !== id));
}, []);
return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onChange={handleChange}
onDelete={handleDelete}
/>
))}
</ul>
);
}When to use useCallback:
- Passing callbacks to memoized child components
- Function is a dependency of useEffect or other hooks
- Optimizing expensive event handlers
React.memo
Memoize entire component to prevent unnecessary re-renders.
Syntax:
const MemoizedComponent = memo(function Component(props) {
// Component logic
}, arePropsEqual?);Example:
import { memo } from 'react';
const ExpensiveComponent = memo(function ExpensiveComponent({ data, onClick }) {
console.log('ExpensiveComponent rendered');
return (
<div>
{data.map(item => (
<div key={item.id} onClick={() => onClick(item)}>
{item.name}
</div>
))}
</div>
);
});
// With custom comparison
const CustomMemoComponent = memo(
function CustomMemoComponent({ user }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => {
// Return true if props are equal (skip re-render)
return prevProps.user.id === nextProps.user.id;
}
);Ref Hooks
useRef
Create mutable reference that persists across renders.
Syntax:
const ref = useRef(initialValue);DOM Reference Example:
import { useRef } from 'react';
function VideoPlayer({ src, isPlaying }) {
const videoRef = useRef(null);
useEffect(() => {
if (isPlaying) {
videoRef.current.play();
} else {
videoRef.current.pause();
}
}, [isPlaying]);
return <video ref={videoRef} src={src} />;
}Storing Mutable Values:
function Timer() {
const intervalRef = useRef(null);
const [count, setCount] = useState(0);
function start() {
if (intervalRef.current) return; // Already running
intervalRef.current = setInterval(() => {
setCount(c => c + 1);
}, 1000);
}
function stop() {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
}
return (
<div>
<p>Count: {count}</p>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
</div>
);
}Previous Value Pattern:
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
// Usage
function Counter({ count }) {
const prevCount = usePrevious(count);
return (
<div>
<p>Current: {count}</p>
<p>Previous: {prevCount}</p>
</div>
);
}Transition Hooks (React 18+)
useTransition
Mark state updates as non-urgent, keeping UI responsive.
Syntax:
const [isPending, startTransition] = useTransition();Example:
import { useState, useTransition } from 'react';
function SearchResults() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
function handleChange(e) {
const value = e.target.value;
setQuery(value); // Urgent update (input value)
startTransition(() => {
// Non-urgent update (search results)
const filtered = performExpensiveSearch(value);
setResults(filtered);
});
}
return (
<div>
<input value={query} onChange={handleChange} />
{isPending && <div>Searching...</div>}
<ul>
{results.map(result => (
<li key={result.id}>{result.name}</li>
))}
</ul>
</div>
);
}useDeferredValue
Defer updating part of the UI.
Syntax:
const deferredValue = useDeferredValue(value);Example:
import { useState, useDeferredValue, memo } from 'react';
const SlowList = memo(function SlowList({ items }) {
// Intentionally slow rendering
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
});
function App() {
const [text, setText] = useState('');
const deferredText = useDeferredValue(text);
return (
<>
<input value={text} onChange={e => setText(e.target.value)} />
<SlowList items={filterItems(deferredText)} />
</>
);
}Server Action Hooks (RSC)
useActionState
Manage server action state and pending status.
Syntax:
const [state, formAction, isPending] = useActionState(serverAction, initialState);Example:
"use client";
import { useActionState } from 'react';
import { updateName } from './actions';
function UpdateNameForm() {
const [state, submitAction, isPending] = useActionState(
updateName,
{ error: null }
);
return (
<form action={submitAction}>
<input type="text" name="name" disabled={isPending} />
{state.error && <span className="error">{state.error}</span>}
<button type="submit" disabled={isPending}>
{isPending ? 'Updating...' : 'Update'}
</button>
</form>
);
}Server Action (actions.js):
"use server";
export async function updateName(prevState, formData) {
const name = formData.get('name');
if (!name) {
return { error: 'Name is required' };
}
try {
await db.users.updateName(name);
return { error: null };
} catch (err) {
return { error: 'Failed to update name' };
}
}Custom Hooks Patterns
Custom hooks let you extract and reuse stateful logic across components.
Naming Convention
Always prefix custom hooks with use:
useCustomHook // ✅ Correct
customHook // ❌ WrongData Fetching Hook
import { useState, useEffect } from 'react';
export function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let ignore = false;
async function fetchData() {
try {
setLoading(true);
setError(null);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (!ignore) {
setData(result);
}
} catch (err) {
if (!ignore) {
setError(err.message);
}
} finally {
if (!ignore) {
setLoading(false);
}
}
}
fetchData();
return () => {
ignore = true;
};
}, [url]);
return { data, loading, error };
}
// Usage
function UserProfile({ userId }) {
const { data: user, loading, error } = useFetch(`/api/users/${userId}`);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>{user.name}</div>;
}Form Input Hook
import { useState } from 'react';
export function useFormInput(initialValue = '') {
const [value, setValue] = useState(initialValue);
function handleChange(e) {
setValue(e.target.value);
}
function reset() {
setValue(initialValue);
}
return {
value,
onChange: handleChange,
reset
};
}
// Usage
function LoginForm() {
const email = useFormInput('');
const password = useFormInput('');
function handleSubmit(e) {
e.preventDefault();
console.log('Email:', email.value);
console.log('Password:', password.value);
email.reset();
password.reset();
}
return (
<form onSubmit={handleSubmit}>
<input type="email" {...email} placeholder="Email" />
<input type="password" {...password} placeholder="Password" />
<button type="submit">Login</button>
</form>
);
}Local Storage Hook
import { useState, useEffect } from 'react';
export function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
useEffect(() => {
try {
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error(error);
}
}, [key, value]);
return [value, setValue];
}
// Usage
function Settings() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
const [language, setLanguage] = useLocalStorage('language', 'en');
return (
<div>
<select value={theme} onChange={e => setTheme(e.target.value)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
<select value={language} onChange={e => setLanguage(e.target.value)}>
<option value="en">English</option>
<option value="es">Spanish</option>
</select>
</div>
);
}Debounce Hook
import { useState, useEffect } from 'react';
export function useDebounce(value, delay = 500) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
// Usage
function SearchComponent() {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearchTerm = useDebounce(searchTerm, 500);
useEffect(() => {
if (debouncedSearchTerm) {
// Perform search
console.log('Searching for:', debouncedSearchTerm);
}
}, [debouncedSearchTerm]);
return (
<input
type="text"
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
placeholder="Search..."
/>
);
}Window Size Hook
import { useState, useEffect } from 'react';
export function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: undefined,
height: undefined,
});
useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
handleResize(); // Set initial size
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return windowSize;
}
// Usage
function ResponsiveComponent() {
const { width, height } = useWindowSize();
return (
<div>
<p>Window width: {width}px</p>
<p>Window height: {height}px</p>
{width < 768 ? <MobileView /> : <DesktopView />}
</div>
);
}Online Status Hook
import { useState, useEffect } from 'react';
export function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
function handleOnline() {
setIsOnline(true);
}
function handleOffline() {
setIsOnline(false);
}
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
}
// Usage
function StatusBar() {
const isOnline = useOnlineStatus();
return (
<div className={isOnline ? 'online' : 'offline'}>
{isOnline ? '✅ Online' : '❌ Offline'}
</div>
);
}Component Patterns
Compound Components
Build components that work together while sharing implicit state.
import { createContext, useContext, useState } from 'react';
const TabsContext = createContext();
export function Tabs({ children, defaultValue }) {
const [activeTab, setActiveTab] = useState(defaultValue);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
}
export function TabList({ children }) {
return <div className="tab-list">{children}</div>;
}
export function Tab({ value, children }) {
const { activeTab, setActiveTab } = useContext(TabsContext);
const isActive = activeTab === value;
return (
<button
className={`tab ${isActive ? 'active' : ''}`}
onClick={() => setActiveTab(value)}
>
{children}
</button>
);
}
export function TabPanels({ children }) {
return <div className="tab-panels">{children}</div>;
}
export function TabPanel({ value, children }) {
const { activeTab } = useContext(TabsContext);
if (activeTab !== value) return null;
return <div className="tab-panel">{children}</div>;
}
// Usage
function App() {
return (
<Tabs defaultValue="tab1">
<TabList>
<Tab value="tab1">Tab 1</Tab>
<Tab value="tab2">Tab 2</Tab>
<Tab value="tab3">Tab 3</Tab>
</TabList>
<TabPanels>
<TabPanel value="tab1">Content for Tab 1</TabPanel>
<TabPanel value="tab2">Content for Tab 2</TabPanel>
<TabPanel value="tab3">Content for Tab 3</TabPanel>
</TabPanels>
</Tabs>
);
}Render Props
Share code between components using a prop whose value is a function.
function DataFetcher({ url, render }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
});
}, [url]);
return render({ data, loading });
}
// Usage
function App() {
return (
<DataFetcher
url="/api/users"
render={({ data, loading }) => (
loading ? <div>Loading...</div> : (
<ul>
{data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
)}
/>
);
}Higher-Order Components (HOC)
Wrap components to enhance functionality.
import { useEffect, useState } from 'react';
// HOC for data fetching
function withData(Component, url) {
return function WithDataComponent(props) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
});
}, []);
return <Component {...props} data={data} loading={loading} />;
};
}
// Original component
function UserList({ data, loading }) {
if (loading) return <div>Loading...</div>;
return (
<ul>
{data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
// Enhanced component
const UserListWithData = withData(UserList, '/api/users');
// Usage
function App() {
return <UserListWithData />;
}Container/Presenter Pattern
Separate logic from presentation.
// Container (logic)
function UserListContainer() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [searchTerm, setSearchTerm] = useState('');
useEffect(() => {
fetchUsers().then(data => {
setUsers(data);
setLoading(false);
});
}, []);
const filteredUsers = users.filter(user =>
user.name.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<UserListPresenter
users={filteredUsers}
loading={loading}
searchTerm={searchTerm}
onSearchChange={setSearchTerm}
/>
);
}
// Presenter (UI)
function UserListPresenter({ users, loading, searchTerm, onSearchChange }) {
if (loading) return <div>Loading...</div>;
return (
<div>
<input
type="text"
value={searchTerm}
onChange={e => onSearchChange(e.target.value)}
placeholder="Search users..."
/>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}Performance Optimization
Identifying Performance Issues
Use React DevTools Profiler to identify:
- Components re-rendering unnecessarily
- Expensive render operations
- State update cascades
Optimization Strategies
1. Memoization
import { memo, useMemo, useCallback } from 'react';
const ExpensiveList = memo(function ExpensiveList({ items, onItemClick }) {
console.log('Rendering ExpensiveList');
return (
<ul>
{items.map(item => (
<li key={item.id} onClick={() => onItemClick(item.id)}>
{item.name}
</li>
))}
</ul>
);
});
function App() {
const [items, setItems] = useState([]);
const [count, setCount] = useState(0);
// Memoize expensive computation
const sortedItems = useMemo(() => {
console.log('Sorting items...');
return [...items].sort((a, b) => a.name.localeCompare(b.name));
}, [items]);
// Memoize callback
const handleItemClick = useCallback((id) => {
console.log('Item clicked:', id);
}, []);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
<ExpensiveList items={sortedItems} onItemClick={handleItemClick} />
</div>
);
}2. Code Splitting
import { lazy, Suspense } from 'react';
// Lazy load components
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const Dashboard = lazy(() => import('./Dashboard'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}
// Route-based splitting
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}3. Virtualization
import { FixedSizeList } from 'react-window';
function VirtualizedList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>
{items[index].name}
</div>
);
return (
<FixedSizeList
height={500}
itemCount={items.length}
itemSize={50}
width="100%"
>
{Row}
</FixedSizeList>
);
}4. Avoid Anonymous Functions
// ❌ Bad - creates new function on every render
function List({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id} onClick={() => console.log(item.id)}>
{item.name}
</li>
))}
</ul>
);
}
// ✅ Good - use useCallback
function List({ items }) {
const handleClick = useCallback((id) => {
console.log(id);
}, []);
return (
<ul>
{items.map(item => (
<li key={item.id} onClick={() => handleClick(item.id)}>
{item.name}
</li>
))}
</ul>
);
}Server Components (RSC)
React Server Components render on the server, reducing client bundle size.
Server Component
// app/users/page.js (Server Component by default)
async function UsersPage() {
// Fetch data directly on server
const users = await db.users.findAll();
return (
<div>
<h1>Users</h1>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
export default UsersPage;Client Component
// components/Counter.js
"use client"; // Mark as client component
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}Composition Pattern
// app/page.js (Server Component)
import Counter from '@/components/Counter'; // Client Component
async function HomePage() {
const data = await fetchData(); // Server-side data fetching
return (
<div>
<h1>{data.title}</h1>
<p>{data.description}</p>
<Counter /> {/* Interactive client component */}
</div>
);
}
export default HomePage;State Management Strategies
Local State (useState)
Best for: Component-specific state
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}Lifted State
Best for: Shared state between sibling components
function Parent() {
const [sharedValue, setSharedValue] = useState('');
return (
<>
<ChildA value={sharedValue} onChange={setSharedValue} />
<ChildB value={sharedValue} />
</>
);
}Context API
Best for: App-wide state (theme, auth, language)
const AuthContext = createContext();
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = async (credentials) => {
const user = await api.login(credentials);
setUser(user);
};
const logout = () => setUser(null);
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}useReducer + Context
Best for: Complex state logic with multiple actions
const StateContext = createContext();
const DispatchContext = createContext();
function reducer(state, action) {
switch (action.type) {
case 'ADD_ITEM':
return { ...state, items: [...state.items, action.payload] };
case 'REMOVE_ITEM':
return { ...state, items: state.items.filter(i => i.id !== action.payload) };
default:
return state;
}
}
function StoreProvider({ children }) {
const [state, dispatch] = useReducer(reducer, { items: [] });
return (
<StateContext.Provider value={state}>
<DispatchContext.Provider value={dispatch}>
{children}
</DispatchContext.Provider>
</StateContext.Provider>
);
}Best Practices
1. Component Design
- Keep components small and focused (Single Responsibility)
- Extract reusable logic into custom hooks
- Prefer composition over inheritance
- Use prop spreading sparingly
- Define PropTypes or TypeScript types
2. State Management
- Keep state as local as possible
- Lift state only when necessary
- Avoid unnecessary state (derive from props when possible)
- Use reducers for complex state logic
- Batch state updates when possible
3. Performance
- Don't optimize prematurely - measure first
- Use React DevTools Profiler
- Memoize expensive computations
- Avoid creating objects/arrays in render
- Use key prop correctly for lists
4. Effects
- Keep effects focused (one concern per effect)
- Always specify dependencies
- Clean up effects (return cleanup function)
- Use AbortController for fetch requests
- Avoid race conditions with ignore flags
5. Code Organization
src/
├── components/
│ ├── common/ # Reusable UI components
│ ├── features/ # Feature-specific components
│ └── layout/ # Layout components
├── hooks/ # Custom hooks
├── context/ # Context providers
├── utils/ # Utility functions
├── services/ # API services
└── types/ # TypeScript typesCommon Anti-Patterns to Avoid
1. Mutating State
// ❌ Wrong
const handleAdd = () => {
items.push(newItem);
setItems(items);
};
// ✅ Correct
const handleAdd = () => {
setItems([...items, newItem]);
};2. Missing Dependencies
// ❌ Wrong
useEffect(() => {
console.log(userId);
}, []); // Missing userId dependency
// ✅ Correct
useEffect(() => {
console.log(userId);
}, [userId]);3. Conditional Hooks
// ❌ Wrong
if (condition) {
const [state, setState] = useState(0);
}
// ✅ Correct
const [state, setState] = useState(0);
if (condition) {
// Use state
}4. Creating Components Inside Components
// ❌ Wrong
function Parent() {
function Child() { // Re-created on every render
return <div>Child</div>;
}
return <Child />;
}
// ✅ Correct
function Child() {
return <div>Child</div>;
}
function Parent() {
return <Child />;
}Troubleshooting
Common Issues
Issue: Infinite re-render loop
- Check useEffect dependencies
- Avoid setting state directly in render
- Use functional updates:
setState(prev => prev + 1)
Issue: Stale closure
- Use functional updates in setState
- Add missing dependencies to useEffect
- Use useRef for mutable values
Issue: Component not re-rendering
- Check if state is being mutated (use immutable updates)
- Verify React.memo comparison function
- Ensure new reference for objects/arrays
Issue: Memory leak warning
- Clean up effects (return cleanup function)
- Cancel ongoing requests on unmount
- Clear timers/intervals
Resources
- React Documentation: https://react.dev
- React Hooks: https://react.dev/reference/react
- React Server Components: https://react.dev/reference/rsc
- React DevTools: https://react.dev/learn/react-developer-tools
- Patterns: https://patterns.dev/react
---
Skill Version: 1.0.0 Last Updated: October 2025 Skill Category: Frontend Development, React, JavaScript Compatible With: React 18+, Next.js 13+, TypeScript
React Patterns - Code Examples
Comprehensive collection of 20+ React component examples demonstrating modern patterns, hooks, and best practices.
Table of Contents
1. State Management Examples 2. Effect Examples 3. Performance Examples 4. Custom Hook Examples 5. Component Pattern Examples 6. Form Examples 7. Data Fetching Examples 8. Advanced Examples
---
State Management Examples
1. Shopping Cart with useState
import { useState } from 'react';
function ShoppingCart() {
const [cart, setCart] = useState([]);
const addItem = (product) => {
const existing = cart.find(item => item.id === product.id);
if (existing) {
setCart(cart.map(item =>
item.id === product.id
? { ...item, quantity: item.quantity + 1 }
: item
));
} else {
setCart([...cart, { ...product, quantity: 1 }]);
}
};
const removeItem = (productId) => {
setCart(cart.filter(item => item.id !== productId));
};
const updateQuantity = (productId, quantity) => {
if (quantity === 0) {
removeItem(productId);
} else {
setCart(cart.map(item =>
item.id === productId ? { ...item, quantity } : item
));
}
};
const total = cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
return (
<div className="shopping-cart">
<h2>Shopping Cart ({cart.length} items)</h2>
{cart.length === 0 ? (
<p>Your cart is empty</p>
) : (
<>
<ul>
{cart.map(item => (
<li key={item.id}>
<div>
<h3>{item.name}</h3>
<p>${item.price}</p>
</div>
<div>
<button onClick={() => updateQuantity(item.id, item.quantity - 1)}>
-
</button>
<span>{item.quantity}</span>
<button onClick={() => updateQuantity(item.id, item.quantity + 1)}>
+
</button>
<button onClick={() => removeItem(item.id)}>Remove</button>
</div>
</li>
))}
</ul>
<div className="cart-total">
<h3>Total: ${total.toFixed(2)}</h3>
<button>Checkout</button>
</div>
</>
)}
</div>
);
}
export default ShoppingCart;2. Multi-Step Form with useReducer
import { useReducer } from 'react';
const initialState = {
step: 1,
formData: {
personalInfo: { name: '', email: '', phone: '' },
address: { street: '', city: '', zipCode: '' },
preferences: { newsletter: false, notifications: true }
},
errors: {}
};
function formReducer(state, action) {
switch (action.type) {
case 'UPDATE_FIELD':
return {
...state,
formData: {
...state.formData,
[action.section]: {
...state.formData[action.section],
[action.field]: action.value
}
}
};
case 'NEXT_STEP':
return { ...state, step: state.step + 1 };
case 'PREV_STEP':
return { ...state, step: state.step - 1 };
case 'SET_ERRORS':
return { ...state, errors: action.errors };
case 'RESET':
return initialState;
default:
return state;
}
}
function MultiStepForm() {
const [state, dispatch] = useReducer(formReducer, initialState);
const updateField = (section, field, value) => {
dispatch({
type: 'UPDATE_FIELD',
section,
field,
value
});
};
const validateStep = () => {
const errors = {};
const { step, formData } = state;
if (step === 1) {
if (!formData.personalInfo.name) errors.name = 'Name is required';
if (!formData.personalInfo.email) errors.email = 'Email is required';
} else if (step === 2) {
if (!formData.address.city) errors.city = 'City is required';
}
dispatch({ type: 'SET_ERRORS', errors });
return Object.keys(errors).length === 0;
};
const handleNext = () => {
if (validateStep()) {
dispatch({ type: 'NEXT_STEP' });
}
};
const handleSubmit = () => {
if (validateStep()) {
console.log('Form submitted:', state.formData);
dispatch({ type: 'RESET' });
}
};
return (
<div className="multi-step-form">
<div className="progress">
Step {state.step} of 3
</div>
{state.step === 1 && (
<div>
<h2>Personal Information</h2>
<input
value={state.formData.personalInfo.name}
onChange={e => updateField('personalInfo', 'name', e.target.value)}
placeholder="Name"
/>
{state.errors.name && <span className="error">{state.errors.name}</span>}
<input
value={state.formData.personalInfo.email}
onChange={e => updateField('personalInfo', 'email', e.target.value)}
placeholder="Email"
/>
{state.errors.email && <span className="error">{state.errors.email}</span>}
<input
value={state.formData.personalInfo.phone}
onChange={e => updateField('personalInfo', 'phone', e.target.value)}
placeholder="Phone"
/>
</div>
)}
{state.step === 2 && (
<div>
<h2>Address</h2>
<input
value={state.formData.address.street}
onChange={e => updateField('address', 'street', e.target.value)}
placeholder="Street"
/>
<input
value={state.formData.address.city}
onChange={e => updateField('address', 'city', e.target.value)}
placeholder="City"
/>
{state.errors.city && <span className="error">{state.errors.city}</span>}
<input
value={state.formData.address.zipCode}
onChange={e => updateField('address', 'zipCode', e.target.value)}
placeholder="Zip Code"
/>
</div>
)}
{state.step === 3 && (
<div>
<h2>Preferences</h2>
<label>
<input
type="checkbox"
checked={state.formData.preferences.newsletter}
onChange={e => updateField('preferences', 'newsletter', e.target.checked)}
/>
Subscribe to newsletter
</label>
<label>
<input
type="checkbox"
checked={state.formData.preferences.notifications}
onChange={e => updateField('preferences', 'notifications', e.target.checked)}
/>
Enable notifications
</label>
</div>
)}
<div className="buttons">
{state.step > 1 && (
<button onClick={() => dispatch({ type: 'PREV_STEP' })}>
Previous
</button>
)}
{state.step < 3 ? (
<button onClick={handleNext}>Next</button>
) : (
<button onClick={handleSubmit}>Submit</button>
)}
</div>
</div>
);
}
export default MultiStepForm;3. Authentication Context
import { createContext, useContext, useState, useEffect } from 'react';
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Check if user is logged in on mount
const token = localStorage.getItem('token');
if (token) {
fetchUser(token);
} else {
setLoading(false);
}
}, []);
const fetchUser = async (token) => {
try {
const response = await fetch('/api/me', {
headers: { Authorization: `Bearer ${token}` }
});
const userData = await response.json();
setUser(userData);
} catch (error) {
console.error('Failed to fetch user:', error);
localStorage.removeItem('token');
} finally {
setLoading(false);
}
};
const login = async (email, password) => {
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
const { user, token } = await response.json();
localStorage.setItem('token', token);
setUser(user);
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
};
const logout = () => {
localStorage.removeItem('token');
setUser(null);
};
const register = async (name, email, password) => {
try {
const response = await fetch('/api/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, email, password })
});
const { user, token } = await response.json();
localStorage.setItem('token', token);
setUser(user);
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
};
const value = {
user,
login,
logout,
register,
isAuthenticated: !!user
};
if (loading) {
return <div>Loading...</div>;
}
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
}
// Usage Example
function LoginPage() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const { login } = useAuth();
const handleSubmit = async (e) => {
e.preventDefault();
setError('');
const result = await login(email, password);
if (!result.success) {
setError(result.error);
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
placeholder="Email"
/>
<input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
placeholder="Password"
/>
{error && <div className="error">{error}</div>}
<button type="submit">Login</button>
</form>
);
}
function ProtectedRoute({ children }) {
const { isAuthenticated } = useAuth();
if (!isAuthenticated) {
return <LoginPage />;
}
return children;
}---
Effect Examples
4. Real-time Chat Component
import { useState, useEffect, useRef } from 'react';
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
const [inputValue, setInputValue] = useState('');
const [isConnected, setIsConnected] = useState(false);
const messagesEndRef = useRef(null);
const wsRef = useRef(null);
// WebSocket connection effect
useEffect(() => {
const ws = new WebSocket(`wss://api.example.com/chat/${roomId}`);
wsRef.current = ws;
ws.onopen = () => {
console.log('Connected to chat');
setIsConnected(true);
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
setMessages(prev => [...prev, message]);
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
ws.onclose = () => {
console.log('Disconnected from chat');
setIsConnected(false);
};
return () => {
ws.close();
};
}, [roomId]);
// Auto-scroll to bottom effect
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
const sendMessage = (e) => {
e.preventDefault();
if (inputValue.trim() && wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(JSON.stringify({
type: 'message',
content: inputValue,
timestamp: Date.now()
}));
setInputValue('');
}
};
return (
<div className="chat-room">
<div className="chat-header">
<h2>Room: {roomId}</h2>
<span className={isConnected ? 'connected' : 'disconnected'}>
{isConnected ? '● Online' : '○ Offline'}
</span>
</div>
<div className="messages">
{messages.map((msg, index) => (
<div key={index} className="message">
<span className="author">{msg.author}</span>
<p>{msg.content}</p>
<span className="timestamp">
{new Date(msg.timestamp).toLocaleTimeString()}
</span>
</div>
))}
<div ref={messagesEndRef} />
</div>
<form onSubmit={sendMessage} className="input-area">
<input
type="text"
value={inputValue}
onChange={e => setInputValue(e.target.value)}
placeholder="Type a message..."
disabled={!isConnected}
/>
<button type="submit" disabled={!isConnected || !inputValue.trim()}>
Send
</button>
</form>
</div>
);
}
export default ChatRoom;5. Intersection Observer Hook
import { useState, useEffect, useRef } from 'react';
function useIntersectionObserver(options = {}) {
const [isIntersecting, setIsIntersecting] = useState(false);
const targetRef = useRef(null);
useEffect(() => {
const target = targetRef.current;
if (!target) return;
const observer = new IntersectionObserver(([entry]) => {
setIsIntersecting(entry.isIntersecting);
}, options);
observer.observe(target);
return () => {
observer.disconnect();
};
}, [options.threshold, options.root, options.rootMargin]);
return [targetRef, isIntersecting];
}
// Lazy Loading Image Component
function LazyImage({ src, alt, placeholder }) {
const [ref, isVisible] = useIntersectionObserver({ threshold: 0.1 });
const [imageSrc, setImageSrc] = useState(placeholder);
useEffect(() => {
if (isVisible && imageSrc === placeholder) {
setImageSrc(src);
}
}, [isVisible, src, imageSrc, placeholder]);
return (
<img
ref={ref}
src={imageSrc}
alt={alt}
style={{
opacity: imageSrc === placeholder ? 0.5 : 1,
transition: 'opacity 0.3s'
}}
/>
);
}
// Infinite Scroll Component
function InfiniteScrollList({ loadMore, hasMore }) {
const [ref, isVisible] = useIntersectionObserver({ threshold: 1.0 });
useEffect(() => {
if (isVisible && hasMore) {
loadMore();
}
}, [isVisible, hasMore, loadMore]);
return (
<div ref={ref} style={{ height: '50px', margin: '20px 0' }}>
{hasMore ? 'Loading more...' : 'No more items'}
</div>
);
}
export { useIntersectionObserver, LazyImage, InfiniteScrollList };6. Document Title Hook
import { useEffect, useRef } from 'react';
function useDocumentTitle(title, retainOnUnmount = false) {
const defaultTitle = useRef(document.title);
useEffect(() => {
document.title = title;
return () => {
if (!retainOnUnmount) {
document.title = defaultTitle.current;
}
};
}, [title, retainOnUnmount]);
}
// Usage Example
function ProductPage({ product }) {
useDocumentTitle(`${product.name} - My Store`);
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
</div>
);
}
function NotificationBadge({ count }) {
const title = count > 0
? `(${count}) New Messages`
: 'My App';
useDocumentTitle(title);
return <div>You have {count} new messages</div>;
}
export default useDocumentTitle;---
Performance Examples
7. Optimized Todo List
import { useState, useMemo, useCallback, memo } from 'react';
const TodoItem = memo(function TodoItem({ todo, onToggle, onDelete }) {
console.log('Rendering TodoItem:', todo.id);
return (
<li className={todo.completed ? 'completed' : ''}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
<span>{todo.text}</span>
<button onClick={() => onDelete(todo.id)}>Delete</button>
</li>
);
});
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Learn React', completed: false },
{ id: 2, text: 'Build a project', completed: false },
{ id: 3, text: 'Master hooks', completed: false }
]);
const [filter, setFilter] = useState('all');
const [searchTerm, setSearchTerm] = useState('');
// Memoized filtered and searched todos
const filteredTodos = useMemo(() => {
console.log('Filtering todos...');
let result = todos;
// Apply search filter
if (searchTerm) {
result = result.filter(todo =>
todo.text.toLowerCase().includes(searchTerm.toLowerCase())
);
}
// Apply completion filter
if (filter === 'active') {
result = result.filter(todo => !todo.completed);
} else if (filter === 'completed') {
result = result.filter(todo => todo.completed);
}
return result;
}, [todos, filter, searchTerm]);
// Memoized statistics
const stats = useMemo(() => ({
total: todos.length,
active: todos.filter(t => !t.completed).length,
completed: todos.filter(t => t.completed).length
}), [todos]);
// Memoized callbacks
const handleToggle = useCallback((id) => {
setTodos(prev => prev.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
}, []);
const handleDelete = useCallback((id) => {
setTodos(prev => prev.filter(todo => todo.id !== id));
}, []);
const handleAdd = useCallback((text) => {
const newTodo = {
id: Date.now(),
text,
completed: false
};
setTodos(prev => [...prev, newTodo]);
}, []);
return (
<div className="todo-list">
<h1>Optimized Todo List</h1>
<div className="stats">
<span>Total: {stats.total}</span>
<span>Active: {stats.active}</span>
<span>Completed: {stats.completed}</span>
</div>
<input
type="text"
placeholder="Search todos..."
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
/>
<div className="filters">
<button
className={filter === 'all' ? 'active' : ''}
onClick={() => setFilter('all')}
>
All
</button>
<button
className={filter === 'active' ? 'active' : ''}
onClick={() => setFilter('active')}
>
Active
</button>
<button
className={filter === 'completed' ? 'active' : ''}
onClick={() => setFilter('completed')}
>
Completed
</button>
</div>
<ul>
{filteredTodos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={handleToggle}
onDelete={handleDelete}
/>
))}
</ul>
<AddTodoForm onAdd={handleAdd} />
</div>
);
}
const AddTodoForm = memo(function AddTodoForm({ onAdd }) {
const [text, setText] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (text.trim()) {
onAdd(text);
setText('');
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={text}
onChange={e => setText(e.target.value)}
placeholder="Add new todo..."
/>
<button type="submit">Add</button>
</form>
);
});
export default TodoList;8. Virtual Scroll List
import { useState, useMemo, useRef, useEffect } from 'react';
function VirtualScrollList({ items, itemHeight = 50, containerHeight = 500 }) {
const [scrollTop, setScrollTop] = useState(0);
const containerRef = useRef(null);
const handleScroll = (e) => {
setScrollTop(e.target.scrollTop);
};
// Calculate which items to render
const { visibleItems, offsetY } = useMemo(() => {
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.ceil((scrollTop + containerHeight) / itemHeight);
const visible = items.slice(startIndex, endIndex);
const offset = startIndex * itemHeight;
return {
visibleItems: visible,
offsetY: offset
};
}, [scrollTop, items, itemHeight, containerHeight]);
const totalHeight = items.length * itemHeight;
return (
<div
ref={containerRef}
onScroll={handleScroll}
style={{
height: containerHeight,
overflow: 'auto',
position: 'relative'
}}
>
<div style={{ height: totalHeight }}>
<div
style={{
transform: `translateY(${offsetY}px)`,
position: 'absolute',
top: 0,
left: 0,
right: 0
}}
>
{visibleItems.map((item, index) => (
<div
key={item.id}
style={{
height: itemHeight,
borderBottom: '1px solid #ccc',
display: 'flex',
alignItems: 'center',
padding: '0 10px'
}}
>
{item.content}
</div>
))}
</div>
</div>
</div>
);
}
// Example usage with large dataset
function VirtualListDemo() {
const items = useMemo(() =>
Array.from({ length: 10000 }, (_, i) => ({
id: i,
content: `Item ${i + 1}`
})),
[]
);
return (
<div>
<h2>Virtual Scroll List (10,000 items)</h2>
<VirtualScrollList items={items} />
</div>
);
}
export default VirtualScrollList;---
Custom Hook Examples
9. useAsync Hook
import { useState, useEffect, useCallback } from 'react';
function useAsync(asyncFunction, immediate = true) {
const [status, setStatus] = useState('idle');
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const execute = useCallback(
async (...params) => {
setStatus('pending');
setData(null);
setError(null);
try {
const response = await asyncFunction(...params);
setData(response);
setStatus('success');
return response;
} catch (err) {
setError(err);
setStatus('error');
throw err;
}
},
[asyncFunction]
);
useEffect(() => {
if (immediate) {
execute();
}
}, [execute, immediate]);
return {
execute,
status,
data,
error,
isIdle: status === 'idle',
isPending: status === 'pending',
isSuccess: status === 'success',
isError: status === 'error'
};
}
// Usage Example
function UserProfile({ userId }) {
const fetchUser = useCallback(
() => fetch(`/api/users/${userId}`).then(res => res.json()),
[userId]
);
const {
data: user,
status,
error,
execute: refetch
} = useAsync(fetchUser);
if (status === 'pending') return <div>Loading...</div>;
if (status === 'error') return <div>Error: {error.message}</div>;
if (!user) return null;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
<button onClick={refetch}>Refresh</button>
</div>
);
}
export default useAsync;10. useMediaQuery Hook
import { useState, useEffect } from 'react';
function useMediaQuery(query) {
const [matches, setMatches] = useState(
() => window.matchMedia(query).matches
);
useEffect(() => {
const mediaQuery = window.matchMedia(query);
const handleChange = (e) => {
setMatches(e.matches);
};
// Modern browsers
if (mediaQuery.addEventListener) {
mediaQuery.addEventListener('change', handleChange);
return () => mediaQuery.removeEventListener('change', handleChange);
}
// Fallback for older browsers
else {
mediaQuery.addListener(handleChange);
return () => mediaQuery.removeListener(handleChange);
}
}, [query]);
return matches;
}
// Breakpoint Hooks
function useBreakpoint() {
const isMobile = useMediaQuery('(max-width: 767px)');
const isTablet = useMediaQuery('(min-width: 768px) and (max-width: 1023px)');
const isDesktop = useMediaQuery('(min-width: 1024px)');
return { isMobile, isTablet, isDesktop };
}
// Usage Example
function ResponsiveLayout() {
const { isMobile, isTablet, isDesktop } = useBreakpoint();
const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');
return (
<div className={prefersDark ? 'dark-mode' : 'light-mode'}>
{isMobile && <MobileLayout />}
{isTablet && <TabletLayout />}
{isDesktop && <DesktopLayout />}
</div>
);
}
export { useMediaQuery, useBreakpoint };11. usePrevious Hook
import { useRef, useEffect } from 'react';
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
// Usage Example
function Counter() {
const [count, setCount] = useState(0);
const prevCount = usePrevious(count);
return (
<div>
<p>Current count: {count}</p>
<p>Previous count: {prevCount}</p>
<p>
{count > prevCount ? '↑ Increased' :
count < prevCount ? '↓ Decreased' :
'− No change'}
</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(count - 1)}>Decrement</button>
</div>
);
}
// Comparison Component
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const prevUserId = usePrevious(userId);
useEffect(() => {
if (userId !== prevUserId) {
console.log(`User changed from ${prevUserId} to ${userId}`);
fetchUser(userId).then(setUser);
}
}, [userId, prevUserId]);
return user ? <div>{user.name}</div> : <div>Loading...</div>;
}
export default usePrevious;12. useToggle Hook
import { useState, useCallback } from 'react';
function useToggle(initialValue = false) {
const [value, setValue] = useState(initialValue);
const toggle = useCallback(() => {
setValue(v => !v);
}, []);
const setTrue = useCallback(() => {
setValue(true);
}, []);
const setFalse = useCallback(() => {
setValue(false);
}, []);
return [value, { toggle, setTrue, setFalse, setValue }];
}
// Usage Examples
function Sidebar() {
const [isOpen, { toggle, setFalse }] = useToggle(false);
return (
<>
<button onClick={toggle}>Toggle Sidebar</button>
<div className={`sidebar ${isOpen ? 'open' : ''}`}>
<button onClick={setFalse}>Close</button>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
</div>
</>
);
}
function AccordionItem({ title, children }) {
const [isExpanded, { toggle }] = useToggle(false);
return (
<div className="accordion-item">
<button onClick={toggle}>
{title}
<span>{isExpanded ? '−' : '+'}</span>
</button>
{isExpanded && (
<div className="accordion-content">
{children}
</div>
)}
</div>
);
}
function Modal({ trigger }) {
const [isVisible, { setTrue: open, setFalse: close }] = useToggle(false);
return (
<>
<div onClick={open}>{trigger}</div>
{isVisible && (
<div className="modal-overlay" onClick={close}>
<div className="modal-content" onClick={e => e.stopPropagation()}>
<button onClick={close}>Close</button>
<div>Modal Content</div>
</div>
</div>
)}
</>
);
}
export default useToggle;---
Component Pattern Examples
13. Accordion Component (Compound Components)
import { createContext, useContext, useState } from 'react';
const AccordionContext = createContext();
function Accordion({ children, allowMultiple = false }) {
const [openItems, setOpenItems] = useState([]);
const toggleItem = (id) => {
setOpenItems(prev => {
if (prev.includes(id)) {
return prev.filter(item => item !== id);
}
if (allowMultiple) {
return [...prev, id];
}
return [id];
});
};
return (
<AccordionContext.Provider value={{ openItems, toggleItem }}>
<div className="accordion">{children}</div>
</AccordionContext.Provider>
);
}
function AccordionItem({ id, children }) {
const { openItems } = useContext(AccordionContext);
const isOpen = openItems.includes(id);
return (
<div className={`accordion-item ${isOpen ? 'open' : ''}`}>
{children}
</div>
);
}
function AccordionHeader({ id, children }) {
const { toggleItem } = useContext(AccordionContext);
return (
<button
className="accordion-header"
onClick={() => toggleItem(id)}
>
{children}
</button>
);
}
function AccordionPanel({ id, children }) {
const { openItems } = useContext(AccordionContext);
const isOpen = openItems.includes(id);
if (!isOpen) return null;
return (
<div className="accordion-panel">
{children}
</div>
);
}
// Compose the exports
Accordion.Item = AccordionItem;
Accordion.Header = AccordionHeader;
Accordion.Panel = AccordionPanel;
// Usage
function FAQPage() {
return (
<Accordion allowMultiple>
<Accordion.Item id="1">
<Accordion.Header id="1">
What is React?
</Accordion.Header>
<Accordion.Panel id="1">
React is a JavaScript library for building user interfaces.
</Accordion.Panel>
</Accordion.Item>
<Accordion.Item id="2">
<Accordion.Header id="2">
What are hooks?
</Accordion.Header>
<Accordion.Panel id="2">
Hooks are functions that let you use state and other React features.
</Accordion.Panel>
</Accordion.Item>
</Accordion>
);
}
export default Accordion;14. Dropdown Menu (Render Props + Compound Components)
import { createContext, useContext, useState, useRef, useEffect } from 'react';
const DropdownContext = createContext();
function Dropdown({ children }) {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef(null);
useEffect(() => {
function handleClickOutside(event) {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setIsOpen(false);
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
const toggle = () => setIsOpen(prev => !prev);
const close = () => setIsOpen(false);
const open = () => setIsOpen(true);
return (
<DropdownContext.Provider value={{ isOpen, toggle, close, open }}>
<div ref={dropdownRef} className="dropdown">
{children}
</div>
</DropdownContext.Provider>
);
}
function DropdownTrigger({ children }) {
const { toggle } = useContext(DropdownContext);
return (
<button onClick={toggle} className="dropdown-trigger">
{children}
</button>
);
}
function DropdownMenu({ children }) {
const { isOpen } = useContext(DropdownContext);
if (!isOpen) return null;
return (
<div className="dropdown-menu">
{children}
</div>
);
}
function DropdownItem({ onClick, children }) {
const { close } = useContext(DropdownContext);
const handleClick = () => {
onClick?.();
close();
};
return (
<button onClick={handleClick} className="dropdown-item">
{children}
</button>
);
}
Dropdown.Trigger = DropdownTrigger;
Dropdown.Menu = DropdownMenu;
Dropdown.Item = DropdownItem;
// Usage
function UserMenu() {
const handleLogout = () => {
console.log('Logging out...');
};
return (
<Dropdown>
<Dropdown.Trigger>
User Menu ▼
</Dropdown.Trigger>
<Dropdown.Menu>
<Dropdown.Item onClick={() => console.log('Profile')}>
Profile
</Dropdown.Item>
<Dropdown.Item onClick={() => console.log('Settings')}>
Settings
</Dropdown.Item>
<Dropdown.Item onClick={handleLogout}>
Logout
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
);
}
export default Dropdown;---
Form Examples
15. Advanced Form with Validation
import { useState } from 'react';
function useForm(initialValues, validate) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const [touched, setTouched] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const handleChange = (name, value) => {
setValues(prev => ({ ...prev, [name]: value }));
// Clear error when user starts typing
if (errors[name]) {
setErrors(prev => ({ ...prev, [name]: undefined }));
}
};
const handleBlur = (name) => {
setTouched(prev => ({ ...prev, [name]: true }));
// Validate on blur
const fieldErrors = validate({ [name]: values[name] });
if (fieldErrors[name]) {
setErrors(prev => ({ ...prev, [name]: fieldErrors[name] }));
}
};
const handleSubmit = async (onSubmit) => {
const validationErrors = validate(values);
if (Object.keys(validationErrors).length > 0) {
setErrors(validationErrors);
setTouched(
Object.keys(initialValues).reduce((acc, key) => {
acc[key] = true;
return acc;
}, {})
);
return;
}
setIsSubmitting(true);
try {
await onSubmit(values);
} catch (err) {
console.error('Form submission error:', err);
} finally {
setIsSubmitting(false);
}
};
const reset = () => {
setValues(initialValues);
setErrors({});
setTouched({});
setIsSubmitting(false);
};
return {
values,
errors,
touched,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
reset
};
}
// Validation functions
const validateRegistrationForm = (values) => {
const errors = {};
if (!values.username) {
errors.username = 'Username is required';
} else if (values.username.length < 3) {
errors.username = 'Username must be at least 3 characters';
}
if (!values.email) {
errors.email = 'Email is required';
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(values.email)) {
errors.email = 'Invalid email address';
}
if (!values.password) {
errors.password = 'Password is required';
} else if (values.password.length < 8) {
errors.password = 'Password must be at least 8 characters';
}
if (values.password !== values.confirmPassword) {
errors.confirmPassword = 'Passwords do not match';
}
if (!values.terms) {
errors.terms = 'You must accept the terms and conditions';
}
return errors;
};
// Registration Form Component
function RegistrationForm() {
const {
values,
errors,
touched,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
reset
} = useForm(
{
username: '',
email: '',
password: '',
confirmPassword: '',
terms: false
},
validateRegistrationForm
);
const onSubmit = async (formValues) => {
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('Form submitted:', formValues);
alert('Registration successful!');
reset();
};
return (
<form
onSubmit={(e) => {
e.preventDefault();
handleSubmit(onSubmit);
}}
className="registration-form"
>
<h2>Register</h2>
<div className="form-group">
<label htmlFor="username">Username</label>
<input
id="username"
type="text"
value={values.username}
onChange={(e) => handleChange('username', e.target.value)}
onBlur={() => handleBlur('username')}
className={touched.username && errors.username ? 'error' : ''}
/>
{touched.username && errors.username && (
<span className="error-message">{errors.username}</span>
)}
</div>
<div className="form-group">
<label htmlFor="email">Email</label>
<input
id="email"
type="email"
value={values.email}
onChange={(e) => handleChange('email', e.target.value)}
onBlur={() => handleBlur('email')}
className={touched.email && errors.email ? 'error' : ''}
/>
{touched.email && errors.email && (
<span className="error-message">{errors.email}</span>
)}
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
id="password"
type="password"
value={values.password}
onChange={(e) => handleChange('password', e.target.value)}
onBlur={() => handleBlur('password')}
className={touched.password && errors.password ? 'error' : ''}
/>
{touched.password && errors.password && (
<span className="error-message">{errors.password}</span>
)}
</div>
<div className="form-group">
<label htmlFor="confirmPassword">Confirm Password</label>
<input
id="confirmPassword"
type="password"
value={values.confirmPassword}
onChange={(e) => handleChange('confirmPassword', e.target.value)}
onBlur={() => handleBlur('confirmPassword')}
className={touched.confirmPassword && errors.confirmPassword ? 'error' : ''}
/>
{touched.confirmPassword && errors.confirmPassword && (
<span className="error-message">{errors.confirmPassword}</span>
)}
</div>
<div className="form-group checkbox">
<label>
<input
type="checkbox"
checked={values.terms}
onChange={(e) => handleChange('terms', e.target.checked)}
onBlur={() => handleBlur('terms')}
/>
I accept the terms and conditions
</label>
{touched.terms && errors.terms && (
<span className="error-message">{errors.terms}</span>
)}
</div>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Registering...' : 'Register'}
</button>
</form>
);
}
export default RegistrationForm;---
Data Fetching Examples
16. Paginated Data Table
import { useState, useEffect, useMemo } from 'react';
function PaginatedTable() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [currentPage, setCurrentPage] = useState(1);
const [itemsPerPage, setItemsPerPage] = useState(10);
const [sortConfig, setSortConfig] = useState({ key: null, direction: 'asc' });
const [searchTerm, setSearchTerm] = useState('');
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
setLoading(true);
try {
const response = await fetch('/api/users');
const result = await response.json();
setData(result);
} catch (error) {
console.error('Failed to fetch data:', error);
} finally {
setLoading(false);
}
};
// Filtered data
const filteredData = useMemo(() => {
if (!searchTerm) return data;
return data.filter(item =>
Object.values(item).some(value =>
String(value).toLowerCase().includes(searchTerm.toLowerCase())
)
);
}, [data, searchTerm]);
// Sorted data
const sortedData = useMemo(() => {
if (!sortConfig.key) return filteredData;
return [...filteredData].sort((a, b) => {
const aValue = a[sortConfig.key];
const bValue = b[sortConfig.key];
if (aValue < bValue) {
return sortConfig.direction === 'asc' ? -1 : 1;
}
if (aValue > bValue) {
return sortConfig.direction === 'asc' ? 1 : -1;
}
return 0;
});
}, [filteredData, sortConfig]);
// Paginated data
const paginatedData = useMemo(() => {
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
return sortedData.slice(startIndex, endIndex);
}, [sortedData, currentPage, itemsPerPage]);
const totalPages = Math.ceil(sortedData.length / itemsPerPage);
const handleSort = (key) => {
setSortConfig(prev => ({
key,
direction: prev.key === key && prev.direction === 'asc' ? 'desc' : 'asc'
}));
};
const getSortIndicator = (key) => {
if (sortConfig.key !== key) return '⇅';
return sortConfig.direction === 'asc' ? '↑' : '↓';
};
if (loading) return <div>Loading...</div>;
return (
<div className="paginated-table">
<div className="controls">
<input
type="text"
placeholder="Search..."
value={searchTerm}
onChange={e => {
setSearchTerm(e.target.value);
setCurrentPage(1); // Reset to first page
}}
/>
<select
value={itemsPerPage}
onChange={e => {
setItemsPerPage(Number(e.target.value));
setCurrentPage(1);
}}
>
<option value={5}>5 per page</option>
<option value={10}>10 per page</option>
<option value={25}>25 per page</option>
<option value={50}>50 per page</option>
</select>
</div>
<table>
<thead>
<tr>
<th onClick={() => handleSort('id')}>
ID {getSortIndicator('id')}
</th>
<th onClick={() => handleSort('name')}>
Name {getSortIndicator('name')}
</th>
<th onClick={() => handleSort('email')}>
Email {getSortIndicator('email')}
</th>
<th onClick={() => handleSort('role')}>
Role {getSortIndicator('role')}
</th>
</tr>
</thead>
<tbody>
{paginatedData.map(item => (
<tr key={item.id}>
<td>{item.id}</td>
<td>{item.name}</td>
<td>{item.email}</td>
<td>{item.role}</td>
</tr>
))}
</tbody>
</table>
<div className="pagination">
<button
onClick={() => setCurrentPage(1)}
disabled={currentPage === 1}
>
First
</button>
<button
onClick={() => setCurrentPage(prev => Math.max(1, prev - 1))}
disabled={currentPage === 1}
>
Previous
</button>
<span>
Page {currentPage} of {totalPages}
</span>
<button
onClick={() => setCurrentPage(prev => Math.min(totalPages, prev + 1))}
disabled={currentPage === totalPages}
>
Next
</button>
<button
onClick={() => setCurrentPage(totalPages)}
disabled={currentPage === totalPages}
>
Last
</button>
</div>
<div className="info">
Showing {((currentPage - 1) * itemsPerPage) + 1} to{' '}
{Math.min(currentPage * itemsPerPage, sortedData.length)} of{' '}
{sortedData.length} entries
{searchTerm && ` (filtered from ${data.length} total entries)`}
</div>
</div>
);
}
export default PaginatedTable;---
Advanced Examples
17. Dark Mode Implementation
import { createContext, useContext, useState, useEffect } from 'react';
const ThemeContext = createContext();
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState(() => {
// Check localStorage first
const saved = localStorage.getItem('theme');
if (saved) return saved;
// Check system preference
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
return 'dark';
}
return 'light';
});
useEffect(() => {
// Save to localStorage
localStorage.setItem('theme', theme);
// Apply to document
document.documentElement.setAttribute('data-theme', theme);
}, [theme]);
// Listen for system theme changes
useEffect(() => {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const handleChange = (e) => {
// Only auto-switch if user hasn't manually set preference
if (!localStorage.getItem('theme')) {
setTheme(e.matches ? 'dark' : 'light');
}
};
mediaQuery.addEventListener('change', handleChange);
return () => mediaQuery.removeEventListener('change', handleChange);
}, []);
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// Theme Toggle Component
function ThemeToggle() {
const { theme, toggleTheme } = useTheme();
return (
<button onClick={toggleTheme} className="theme-toggle">
{theme === 'light' ? '🌙 Dark' : '☀️ Light'}
</button>
);
}
export default ThemeToggle;18. File Upload with Progress
import { useState, useRef } from 'react';
function FileUpload({ onUploadComplete }) {
const [files, setFiles] = useState([]);
const [uploading, setUploading] = useState(false);
const fileInputRef = useRef(null);
const handleFileSelect = (e) => {
const selectedFiles = Array.from(e.target.files);
const filesWithProgress = selectedFiles.map(file => ({
file,
progress: 0,
status: 'pending', // pending, uploading, completed, error
id: Math.random().toString(36).substr(2, 9)
}));
setFiles(prev => [...prev, ...filesWithProgress]);
};
const uploadFile = async (fileData) => {
const formData = new FormData();
formData.append('file', fileData.file);
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
const progress = Math.round((e.loaded / e.total) * 100);
setFiles(prev => prev.map(f =>
f.id === fileData.id
? { ...f, progress, status: 'uploading' }
: f
));
}
});
xhr.addEventListener('load', () => {
if (xhr.status === 200) {
setFiles(prev => prev.map(f =>
f.id === fileData.id
? { ...f, status: 'completed', progress: 100 }
: f
));
resolve(xhr.response);
} else {
reject(new Error('Upload failed'));
}
});
xhr.addEventListener('error', () => {
setFiles(prev => prev.map(f =>
f.id === fileData.id
? { ...f, status: 'error' }
: f
));
reject(new Error('Upload failed'));
});
xhr.open('POST', '/api/upload');
xhr.send(formData);
});
};
const handleUpload = async () => {
setUploading(true);
try {
const pendingFiles = files.filter(f => f.status === 'pending');
await Promise.all(pendingFiles.map(file => uploadFile(file)));
onUploadComplete?.();
} catch (error) {
console.error('Upload error:', error);
} finally {
setUploading(false);
}
};
const removeFile = (id) => {
setFiles(prev => prev.filter(f => f.id !== id));
};
const formatFileSize = (bytes) => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
};
return (
<div className="file-upload">
<input
ref={fileInputRef}
type="file"
multiple
onChange={handleFileSelect}
style={{ display: 'none' }}
/>
<button onClick={() => fileInputRef.current?.click()}>
Select Files
</button>
{files.length > 0 && (
<>
<div className="file-list">
{files.map(({ id, file, progress, status }) => (
<div key={id} className="file-item">
<div className="file-info">
<span className="file-name">{file.name}</span>
<span className="file-size">{formatFileSize(file.size)}</span>
</div>
<div className="file-progress">
<div
className={`progress-bar ${status}`}
style={{ width: `${progress}%` }}
/>
</div>
<div className="file-status">
{status === 'pending' && 'Pending'}
{status === 'uploading' && `${progress}%`}
{status === 'completed' && '✓ Completed'}
{status === 'error' && '✗ Failed'}
</div>
{status !== 'uploading' && (
<button onClick={() => removeFile(id)}>Remove</button>
)}
</div>
))}
</div>
<button
onClick={handleUpload}
disabled={uploading || !files.some(f => f.status === 'pending')}
>
{uploading ? 'Uploading...' : 'Upload All'}
</button>
</>
)}
</div>
);
}
export default FileUpload;19. Drag and Drop List
import { useState } from 'react';
function DragDropList({ initialItems }) {
const [items, setItems] = useState(initialItems);
const [draggedItem, setDraggedItem] = useState(null);
const handleDragStart = (e, index) => {
setDraggedItem(index);
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', e.target);
};
const handleDragOver = (e, index) => {
e.preventDefault();
if (draggedItem === null || draggedItem === index) return;
const newItems = [...items];
const draggedContent = newItems[draggedItem];
newItems.splice(draggedItem, 1);
newItems.splice(index, 0, draggedContent);
setDraggedItem(index);
setItems(newItems);
};
const handleDragEnd = () => {
setDraggedItem(null);
};
return (
<div className="drag-drop-list">
<h2>Drag to Reorder</h2>
<ul>
{items.map((item, index) => (
<li
key={item.id}
draggable
onDragStart={(e) => handleDragStart(e, index)}
onDragOver={(e) => handleDragOver(e, index)}
onDragEnd={handleDragEnd}
className={draggedItem === index ? 'dragging' : ''}
>
<span className="drag-handle">⋮⋮</span>
<span className="item-content">{item.content}</span>
</li>
))}
</ul>
</div>
);
}
// Usage
function App() {
const items = [
{ id: 1, content: 'First item' },
{ id: 2, content: 'Second item' },
{ id: 3, content: 'Third item' },
{ id: 4, content: 'Fourth item' },
{ id: 5, content: 'Fifth item' }
];
return <DragDropList initialItems={items} />;
}
export default DragDropList;20. Notification System
import { createContext, useContext, useState, useCallback } from 'react';
const NotificationContext = createContext();
export function NotificationProvider({ children }) {
const [notifications, setNotifications] = useState([]);
const addNotification = useCallback((message, type = 'info', duration = 3000) => {
const id = Date.now() + Math.random();
const notification = {
id,
message,
type, // info, success, warning, error
duration
};
setNotifications(prev => [...prev, notification]);
if (duration > 0) {
setTimeout(() => {
removeNotification(id);
}, duration);
}
return id;
}, []);
const removeNotification = useCallback((id) => {
setNotifications(prev => prev.filter(n => n.id !== id));
}, []);
return (
<NotificationContext.Provider value={{ addNotification, removeNotification }}>
{children}
<NotificationContainer notifications={notifications} onClose={removeNotification} />
</NotificationContext.Provider>
);
}
export function useNotification() {
const context = useContext(NotificationContext);
if (!context) {
throw new Error('useNotification must be used within NotificationProvider');
}
return context;
}
function NotificationContainer({ notifications, onClose }) {
return (
<div className="notification-container">
{notifications.map(notification => (
<Notification
key={notification.id}
notification={notification}
onClose={() => onClose(notification.id)}
/>
))}
</div>
);
}
function Notification({ notification, onClose }) {
const { message, type } = notification;
const getIcon = () => {
switch (type) {
case 'success': return '✓';
case 'error': return '✗';
case 'warning': return '⚠';
default: return 'ℹ';
}
};
return (
<div className={`notification ${type}`}>
<span className="icon">{getIcon()}</span>
<span className="message">{message}</span>
<button onClick={onClose} className="close">×</button>
</div>
);
}
// Usage Example
function MyComponent() {
const { addNotification } = useNotification();
const handleSuccess = () => {
addNotification('Operation completed successfully!', 'success');
};
const handleError = () => {
addNotification('Something went wrong!', 'error', 5000);
};
return (
<div>
<button onClick={handleSuccess}>Show Success</button>
<button onClick={handleError}>Show Error</button>
</div>
);
}
export default NotificationProvider;---
This collection provides practical, real-world examples covering the full spectrum of modern React development patterns. Each example is production-ready and demonstrates best practices for hooks, state management, performance optimization, and component composition.
React Patterns Skill
A comprehensive skill for modern React development covering hooks, component patterns, state management, and performance optimization.
Overview
This skill provides complete guidance for building scalable React applications using modern patterns and best practices. It covers React 18+ features, including Server Components, Suspense, and Concurrent Features.
What's Included
Core Topics
1. React Hooks - Complete reference for all built-in hooks
- State hooks (useState, useReducer, useContext)
- Effect hooks (useEffect, useLayoutEffect)
- Performance hooks (useMemo, useCallback, memo)
- Ref hooks (useRef, useImperativeHandle)
- Transition hooks (useTransition, useDeferredValue)
- Server action hooks (useActionState)
2. Custom Hooks - Reusable patterns for common scenarios
- Data fetching hooks
- Form management hooks
- Browser API hooks (localStorage, windowSize, onlineStatus)
- Utility hooks (debounce, previous value, etc.)
3. Component Patterns - Advanced composition techniques
- Compound components
- Render props
- Higher-Order Components (HOC)
- Container/Presenter pattern
4. Performance Optimization - Techniques to improve app speed
- Memoization strategies
- Code splitting and lazy loading
- Virtualization for large lists
- Avoiding common pitfalls
5. Server Components - React Server Components (RSC)
- Server vs Client components
- Composition patterns
- Data fetching strategies
- Server actions
6. State Management - Strategies for different scales
- Local state patterns
- Lifted state
- Context API
- useReducer + Context for complex state
Quick Start
Installation
This skill is already installed in your Claude skills directory. Use it by invoking React-related development tasks.
Basic Usage
The skill provides guidance for:
// State management
const [count, setCount] = useState(0);
// Side effects
useEffect(() => {
// Effect logic
return () => {
// Cleanup
};
}, [dependencies]);
// Performance optimization
const memoizedValue = useMemo(() => computeExpensive(a, b), [a, b]);
const memoizedCallback = useCallback(() => doSomething(a, b), [a, b]);
// Custom hooks
const { data, loading, error } = useFetch('/api/users');Key Features
Comprehensive Hook Coverage
Every built-in React hook is documented with:
- Complete syntax reference
- Multiple real-world examples
- Best practices and use cases
- Common pitfalls to avoid
Custom Hook Library
Pre-built custom hooks for common scenarios:
useFetch- Data fetching with loading and error statesuseFormInput- Form field managementuseLocalStorage- Persistent stateuseDebounce- Debounced valuesuseWindowSize- Responsive breakpointsuseOnlineStatus- Network status detection
Design Patterns
Modern React patterns including:
- Compound components for flexible APIs
- Render props for code sharing
- Container/Presenter separation
- HOCs for cross-cutting concerns
Performance Strategies
Proven optimization techniques:
- React.memo for component memoization
- useMemo for expensive calculations
- useCallback for stable function references
- Code splitting with React.lazy
- Virtualization for large datasets
Server Components
Complete guide to React Server Components:
- Understanding Server vs Client components
- Async Server Components
- Composition patterns
- Server Actions with useActionState
When to Use This Skill
Use this skill when you need to:
- Build new React applications from scratch
- Refactor existing React code to modern patterns
- Implement complex state management
- Optimize React application performance
- Create reusable custom hooks
- Understand React Server Components
- Debug React-specific issues
- Learn best practices and patterns
Examples Included
The skill includes 15+ complete code examples:
1. Counter with useState - Basic state management 2. Task Manager with useReducer - Complex state logic 3. Theme Context - Global state with Context API 4. Data Fetching with useEffect - Async operations 5. Chat Room Subscription - Effect cleanup 6. Todo List with Filters - useMemo optimization 7. Memoized Callbacks - useCallback with React.memo 8. Video Player - useRef for DOM manipulation 9. Search with Transitions - useTransition for responsiveness 10. Custom useFetch Hook - Reusable data fetching 11. Form Management Hook - Input handling 12. Compound Tabs Component - Advanced composition 13. Virtualized List - Performance for large datasets 14. Server Component - RSC data fetching 15. Server Action Form - useActionState integration
File Structure
react-patterns/
├── SKILL.md # Complete skill documentation (25KB+)
├── README.md # This file (10KB)
└── EXAMPLES.md # Additional examples (20KB)Best Practices Covered
Component Design
- Single Responsibility Principle
- Composition over inheritance
- Proper prop handling
- Type safety considerations
State Management
- Keep state local when possible
- Lift state only when needed
- Avoid derived state
- Use reducers for complex logic
Performance
- Measure before optimizing
- Use React DevTools Profiler
- Memoize expensive operations
- Optimize re-renders
Effects
- One concern per effect
- Always specify dependencies
- Clean up side effects
- Handle race conditions
Code Organization
- Logical file structure
- Separation of concerns
- Reusable components
- Custom hooks for shared logic
Common Patterns
Data Fetching
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let ignore = false;
async function fetchUser() {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
if (!ignore) {
setUser(data);
setLoading(false);
}
}
fetchUser();
return () => {
ignore = true;
};
}, [userId]);
if (loading) return <div>Loading...</div>;
return <div>{user.name}</div>;
}Form Handling
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
function handleSubmit(e) {
e.preventDefault();
// Handle login
}
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
/>
<input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
/>
<button type="submit">Login</button>
</form>
);
}Global State
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
function useTheme() {
return useContext(ThemeContext);
}Anti-Patterns to Avoid
1. Mutating State
// ❌ Wrong
items.push(newItem);
setItems(items);
// ✅ Correct
setItems([...items, newItem]);2. Missing Dependencies
// ❌ Wrong
useEffect(() => {
console.log(userId);
}, []);
// ✅ Correct
useEffect(() => {
console.log(userId);
}, [userId]);3. Conditional Hooks
// ❌ Wrong
if (condition) {
const [state, setState] = useState(0);
}
// ✅ Correct
const [state, setState] = useState(0);Troubleshooting Guide
Infinite Re-renders
- Check useEffect dependencies
- Avoid setting state in render
- Use functional updates
Stale Closures
- Use functional updates
- Add dependencies to effects
- Use useRef for mutable values
Memory Leaks
- Clean up effects
- Cancel ongoing requests
- Clear timers/intervals
Performance Issues
- Use React DevTools Profiler
- Memoize expensive computations
- Avoid anonymous functions in render
- Implement virtualization for large lists
Resources
Official Documentation
- React Docs - Official React documentation
- React Hooks - Complete hooks API reference
- Server Components - RSC documentation
Tools
- React DevTools - Browser extension
- Create React App - Quick start tool
- Vite - Fast build tool
Learning Resources
- React Patterns - Design patterns
- React TypeScript Cheatsheet - TypeScript guide
- React Testing Library - Testing guide
Version Information
- Skill Version: 1.0.0
- Last Updated: October 2025
- React Version: 18+
- Compatibility: React 18+, Next.js 13+, TypeScript
Contributing
This skill is maintained as part of Claude's skill library. For suggestions or improvements, please provide feedback through your Claude interface.
License
This skill documentation is provided as-is for educational and development purposes.
---
Quick Reference
Import Statements
import { useState, useEffect, useContext, useReducer } from 'react';
import { memo, useMemo, useCallback, useRef } from 'react';
import { useTransition, useDeferredValue } from 'react';
import { lazy, Suspense } from 'react';Common Hooks
const [state, setState] = useState(initialValue);
const [state, dispatch] = useReducer(reducer, initialState);
const value = useContext(Context);
const memoizedValue = useMemo(() => compute(), [deps]);
const memoizedCallback = useCallback(() => {}, [deps]);
const ref = useRef(initialValue);Effect Patterns
// Mount only
useEffect(() => { /* ... */ }, []);
// Every render
useEffect(() => { /* ... */ });
// When deps change
useEffect(() => { /* ... */ }, [dep1, dep2]);
// With cleanup
useEffect(() => {
// Setup
return () => {
// Cleanup
};
}, [deps]);Performance Patterns
// Memoize component
const MemoComponent = memo(Component);
// Memoize value
const value = useMemo(() => expensive(), [deps]);
// Memoize callback
const callback = useCallback(() => {}, [deps]);
// Lazy load
const Component = lazy(() => import('./Component'));Related skills
How it compares
Use react-patterns for architectural React guidance rather than Node.js API scaffolding or git cleanup tasks.
FAQ
What problems does react-patterns address?
react-patterns addresses composition, state boundary placement, rendering performance, and maintainability in React apps whose component trees and shared state outgrow simple early prototypes.
When should developers use react-patterns?
react-patterns fits refactors or feature modules where prop drilling, redundant re-renders, or unclear state ownership block velocity in medium-to-large React codebases.