Skill v1.0.1
currentAutomated scan100/1001 files
version: "1.0.1" name: react-patterns description: Modern React development with hooks, component patterns, state management, and performance optimization for building scalable applications
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
- Presentational Components: Focus on UI, receive data via props
- Container Components: Handle logic, state, and side effects
- Server Components: Render on the server, no client JavaScript
- Client Components: Interactive components marked with
"use client" - 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 renderconst [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} /><TaskListtasks={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;}// Usagefunction Button() {const { theme, toggleTheme } = useTheme();return (<buttononClick={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 componentsfunction 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 logicreturn () => {// 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 conditionsasync 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 mountuseEffect(() => {console.log('Component mounted');}, []); // Empty dependency array// Run on every renderuseEffect(() => {console.log('Component rendered');}); // No dependency array// Run when specific values changeuseEffect(() => {console.log('userId changed:', userId);}, [userId]); // Dependency array with values// Cleanup on unmountuseEffect(() => {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 changesconst 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 calculationsconst sum = useMemo(() => a + b, [a, b]); // Overkill// ✅ Just compute directlyconst 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><inputtype="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-rendersconst 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 => (<TodoItemkey={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 comparisonconst 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 runningintervalRef.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;}// Usagefunction 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 renderingreturn (<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 // ✅ CorrectcustomHook // ❌ Wrong
Data 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 };}// Usagefunction 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};}// Usagefunction 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];}// Usagefunction 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;}// Usagefunction SearchComponent() {const [searchTerm, setSearchTerm] = useState('');const debouncedSearchTerm = useDebounce(searchTerm, 500);useEffect(() => {if (debouncedSearchTerm) {// Perform searchconsole.log('Searching for:', debouncedSearchTerm);}}, [debouncedSearchTerm]);return (<inputtype="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 sizewindow.addEventListener('resize', handleResize);return () => window.removeEventListener('resize', handleResize);}, []);return windowSize;}// Usagefunction 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;}// Usagefunction 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 (<buttonclassName={`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>;}// Usagefunction 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 });}// Usagefunction App() {return (<DataFetcherurl="/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 fetchingfunction 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 componentfunction UserList({ data, loading }) {if (loading) return <div>Loading...</div>;return (<ul>{data?.map(user => (<li key={user.id}>{user.name}</li>))}</ul>);}// Enhanced componentconst UserListWithData = withData(UserList, '/api/users');// Usagefunction 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 (<UserListPresenterusers={filteredUsers}loading={loading}searchTerm={searchTerm}onSearchChange={setSearchTerm}/>);}// Presenter (UI)function UserListPresenter({ users, loading, searchTerm, onSearchChange }) {if (loading) return <div>Loading...</div>;return (<div><inputtype="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 computationconst sortedItems = useMemo(() => {console.log('Sorting items...');return [...items].sort((a, b) => a.name.localeCompare(b.name));}, [items]);// Memoize callbackconst 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 componentsconst HeavyComponent = lazy(() => import('./HeavyComponent'));const Dashboard = lazy(() => import('./Dashboard'));function App() {return (<Suspense fallback={<div>Loading...</div>}><HeavyComponent /></Suspense>);}// Route-based splittingimport { 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 (<FixedSizeListheight={500}itemCount={items.length}itemSize={50}width="100%">{Row}</FixedSizeList>);}
4. Avoid Anonymous Functions
// ❌ Bad - creates new function on every renderfunction List({ items }) {return (<ul>{items.map(item => (<li key={item.id} onClick={() => console.log(item.id)}>{item.name}</li>))}</ul>);}// ✅ Good - use useCallbackfunction 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 serverconst 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 componentimport { 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 Componentasync function HomePage() {const data = await fetchData(); // Server-side data fetchingreturn (<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 types
Common Anti-Patterns to Avoid
1. Mutating State
// ❌ Wrongconst handleAdd = () => {items.push(newItem);setItems(items);};// ✅ Correctconst handleAdd = () => {setItems([...items, newItem]);};
2. Missing Dependencies
// ❌ WronguseEffect(() => {console.log(userId);}, []); // Missing userId dependency// ✅ CorrectuseEffect(() => {console.log(userId);}, [userId]);
3. Conditional Hooks
// ❌ Wrongif (condition) {const [state, setState] = useState(0);}// ✅ Correctconst [state, setState] = useState(0);if (condition) {// Use state}
4. Creating Components Inside Components
// ❌ Wrongfunction Parent() {function Child() { // Re-created on every renderreturn <div>Child</div>;}return <Child />;}// ✅ Correctfunction 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