React Patterns & Best Practices

Design patterns are the building blocks of professional React applications. They represent years of collective wisdom from the React community, solving common problems in elegant, reusable ways. This comprehensive guide covers essential patterns from basic component composition to advanced performance optimizations, helping you write more maintainable, scalable, and efficient React code.

Why Learn React Patterns?

🏗️ Better Architecture

Patterns provide proven solutions to common architectural challenges, helping you build more robust applications from the start.

🚀 Improved Performance

Performance patterns help you optimize rendering, reduce bundle size, and create faster, more responsive user interfaces.

👥 Team Collaboration

Common patterns create a shared vocabulary and understanding within teams, making code reviews and collaboration more effective.

🔧 Maintainability

Well-established patterns lead to more predictable, testable code that's easier to maintain and extend over time.

Advertisement Space - top-patterns

Google AdSense: horizontal

Component Patterns

Container and Presentational Components

One of the foundational patterns in React, this approach separates your components into two distinct categories: containers that manage state and logic, and presentational components that focus purely on rendering UI. This separation makes your code more modular, testable, and reusable. While modern React with hooks has made this pattern less rigid, understanding it is crucial for organizing complex applications and maintaining clean component boundaries.

Benefits

  • Clear separation of concerns
  • Easier to test presentational components
  • Better reusability
  • Simpler component structure

Use Cases

When you need to separate data fetching logic from UI rendering

Implementation

1// Container Component (Logic)
2import React, { useState, useEffect } from 'react';
3import UserList from './UserList';
4
5const UserListContainer = () => {
6 const [users, setUsers] = useState([]);
7 const [loading, setLoading] = useState(true);
8 const [error, setError] = useState(null);
9
10 useEffect(() => {
11 const fetchUsers = async () => {
12 try {
13 const response = await fetch('/api/users');
14 const userData = await response.json();
15 setUsers(userData);
16 } catch (err) {
17 setError(err.message);
18 } finally {
19 setLoading(false);
20 }
21 };
22
23 fetchUsers();
24 }, []);
25
26 const handleUserDelete = (userId) => {
27 setUsers(users.filter(user => user.id !== userId));
28 };
29
30 return (
31 <UserList
32 users={users}
33 loading={loading}
34 error={error}
35 onUserDelete={handleUserDelete}
36 />
37 );
38};
39
40// Presentational Component (UI)
41const UserList = ({ users, loading, error, onUserDelete }) => {
42 if (loading) return <div className="spinner">Loading...</div>;
43 if (error) return <div className="error">Error: {error}</div>;
44 if (users.length === 0) return <div>No users found</div>;
45
46 return (
47 <div className="user-list">
48 <h2>Users</h2>
49 {users.map(user => (
50 <div key={user.id} className="user-card">
51 <h3>{user.name}</h3>
52 <p>{user.email}</p>
53 <button onClick={() => onUserDelete(user.id)}>
54 Delete
55 </button>
56 </div>
57 ))}
58 </div>
59 );
60};
Component Patterns

Higher-Order Components (HOCs)

Higher-Order Components are a powerful pattern for reusing component logic by wrapping components with additional functionality. Think of them as decorators for your components - they take a component and return an enhanced version with extra props, behavior, or rendering logic. While hooks have replaced many HOC use cases, they remain valuable for cross-cutting concerns like authentication, logging, and feature flags. Understanding HOCs also helps when working with popular libraries like React Router and Redux.

Benefits

  • Code reusability across components
  • Separation of cross-cutting concerns
  • Composable functionality
  • Consistent behavior across components

Use Cases

Authentication, logging, error handling, loading states

Implementation

1// Higher-Order Component
2import React from 'react';
3
4const withLoading = (WrappedComponent) => {
5 return function WithLoadingComponent(props) {
6 if (props.isLoading) {
7 return (
8 <div className="loading-wrapper">
9 <div className="spinner">Loading...</div>
10 </div>
11 );
12 }
13 return <WrappedComponent {...props} />;
14 };
15};
16
17const withError = (WrappedComponent) => {
18 return function WithErrorComponent(props) {
19 if (props.error) {
20 return (
21 <div className="error-wrapper">
22 <h3>Something went wrong</h3>
23 <p>{props.error}</p>
24 </div>
25 );
26 }
27 return <WrappedComponent {...props} />;
28 };
29};
30
31// Usage
32const UserProfile = ({ user }) => (
33 <div>
34 <h2>{user.name}</h2>
35 <p>{user.email}</p>
36 </div>
37);
38
39const EnhancedUserProfile = withError(withLoading(UserProfile));
40
41// Component usage
42function App() {
43 const [user, setUser] = useState(null);
44 const [isLoading, setIsLoading] = useState(true);
45 const [error, setError] = useState(null);
46
47 useEffect(() => {
48 fetchUser()
49 .then(setUser)
50 .catch(setError)
51 .finally(() => setIsLoading(false));
52 }, []);
53
54 return (
55 <EnhancedUserProfile
56 user={user}
57 isLoading={isLoading}
58 error={error}
59 />
60 );
61}
Component Patterns

Render Props Pattern

A component that uses a function prop to determine what to render

Benefits

  • Flexible and reusable logic
  • Dynamic rendering based on state
  • Inversion of control
  • Composable patterns

Use Cases

Data fetching, mouse tracking, form validation, conditional rendering

Implementation

1// Render Props Component
2class MouseTracker extends React.Component {
3 state = { x: 0, y: 0 };
4
5 handleMouseMove = (event) => {
6 this.setState({
7 x: event.clientX,
8 y: event.clientY
9 });
10 };
11
12 render() {
13 return (
14 <div onMouseMove={this.handleMouseMove}>
15 {this.props.render(this.state)}
16 </div>
17 );
18 }
19}
20
21// Modern Hook-based version
22const useMousePosition = () => {
23 const [position, setPosition] = useState({ x: 0, y: 0 });
24
25 useEffect(() => {
26 const handleMouseMove = (event) => {
27 setPosition({
28 x: event.clientX,
29 y: event.clientY
30 });
31 };
32
33 window.addEventListener('mousemove', handleMouseMove);
34 return () => window.removeEventListener('mousemove', handleMouseMove);
35 }, []);
36
37 return position;
38};
39
40const MouseTracker = ({ children }) => {
41 const position = useMousePosition();
42 return (
43 <div>
44 {children(position)}
45 </div>
46 );
47};
48
49// Usage
50function App() {
51 return (
52 <div>
53 <h1>Mouse Tracker</h1>
54 <MouseTracker>
55 {({ x, y }) => (
56 <div>
57 <h2>Mouse position:</h2>
58 <p>X: {x}, Y: {y}</p>
59 </div>
60 )}
61 </MouseTracker>
62 </div>
63 );
64}

Advertisement Space - mid-patterns

Google AdSense: rectangle

State Management

Compound Component Pattern

Components that work together to create a cohesive interface

Benefits

  • Intuitive API design
  • Flexible component composition
  • Better developer experience
  • Clear component relationships

Use Cases

Tabs, accordions, dropdown menus, form builders

Implementation

1// Compound Component Example
2const Tabs = ({ children, defaultTab = 0 }) => {
3 const [activeTab, setActiveTab] = useState(defaultTab);
4
5 const enhancedChildren = React.Children.map(children, (child, index) => {
6 if (React.isValidElement(child)) {
7 return React.cloneElement(child, {
8 isActive: index === activeTab,
9 onSelect: () => setActiveTab(index),
10 index
11 });
12 }
13 return child;
14 });
15
16 return <div className="tabs">{enhancedChildren}</div>;
17};
18
19const TabList = ({ children }) => (
20 <div className="tab-list" role="tablist">
21 {children}
22 </div>
23);
24
25const Tab = ({ children, isActive, onSelect, index }) => (
26 <button
27 className={isActive ? 'tab active' : 'tab'}
28 onClick={onSelect}
29 role="tab"
30 aria-selected={isActive}
31 aria-controls={panel-$}{index}}
32 >
33 {children}
34 </button>
35);
36
37const TabPanels = ({ children }) => (
38 <div className="tab-panels">
39 {children}
40 </div>
41);
42
43const TabPanel = ({ children, isActive, index }) => (
44 <div
45 className={isActive ? 'tab-panel active' : 'tab-panel'}
46 role="tabpanel"
47 id={panel-$}{index}}
48 hidden={!isActive}
49 >
50 {children}
51 </div>
52);
53
54// Attach sub-components
55Tabs.TabList = TabList;
56Tabs.Tab = Tab;
57Tabs.TabPanels = TabPanels;
58Tabs.TabPanel = TabPanel;
59
60// Usage
61function App() {
62 return (
63 <Tabs defaultTab={0}>
64 <Tabs.TabList>
65 <Tabs.Tab>Tab 1</Tabs.Tab>
66 <Tabs.Tab>Tab 2</Tabs.Tab>
67 <Tabs.Tab>Tab 3</Tabs.Tab>
68 </Tabs.TabList>
69
70 <Tabs.TabPanels>
71 <Tabs.TabPanel>
72 <h2>Content 1</h2>
73 <p>This is the content for tab 1.</p>
74 </Tabs.TabPanel>
75 <Tabs.TabPanel>
76 <h2>Content 2</h2>
77 <p>This is the content for tab 2.</p>
78 </Tabs.TabPanel>
79 <Tabs.TabPanel>
80 <h2>Content 3</h2>
81 <p>This is the content for tab 3.</p>
82 </Tabs.TabPanel>
83 </Tabs.TabPanels>
84 </Tabs>
85 );
86}
State Management

Provider Pattern

The Provider pattern leverages React Context to share data and functionality across your component tree without prop drilling. It's the foundation of many state management libraries and is essential for building scalable React applications. This pattern encapsulates global state, provides a clean API for consumers, and can be composed to create powerful state management solutions. Understanding providers is crucial for working with themes, authentication, internationalization, and any cross-cutting concerns in your application.

Benefits

  • Centralized state management
  • Avoid prop drilling
  • Predictable state updates
  • Easy to test and debug

Use Cases

Global state, user authentication, theme management, shopping cart

Implementation

1// Provider Pattern Implementation
2import React, { createContext, useContext, useReducer } from 'react';
3
4// Action types
5const ACTIONS = {
6 ADD_ITEM: 'ADD_ITEM',
7 REMOVE_ITEM: 'REMOVE_ITEM',
8 TOGGLE_ITEM: 'TOGGLE_ITEM',
9 CLEAR_ITEMS: 'CLEAR_ITEMS'
10};
11
12// Reducer
13const todoReducer = (state, action) => {
14 switch (action.type) {
15 case ACTIONS.ADD_ITEM:
16 return {
17 ...state,
18 items: [...state.items, {
19 id: Date.now(),
20 text: action.payload,
21 completed: false
22 }]
23 };
24
25 case ACTIONS.REMOVE_ITEM:
26 return {
27 ...state,
28 items: state.items.filter(item => item.id !== action.payload)
29 };
30
31 case ACTIONS.TOGGLE_ITEM:
32 return {
33 ...state,
34 items: state.items.map(item =>
35 item.id === action.payload
36 ? { ...item, completed: !item.completed }
37 : item
38 )
39 };
40
41 case ACTIONS.CLEAR_ITEMS:
42 return { ...state, items: [] };
43
44 default:
45 return state;
46 }
47};
48
49// Context
50const TodoContext = createContext();
51
52// Provider Component
53export const TodoProvider = ({ children }) => {
54 const [state, dispatch] = useReducer(todoReducer, {
55 items: [],
56 filter: 'all'
57 });
58
59 const actions = {
60 addItem: (text) => dispatch({ type: ACTIONS.ADD_ITEM, payload: text }),
61 removeItem: (id) => dispatch({ type: ACTIONS.REMOVE_ITEM, payload: id }),
62 toggleItem: (id) => dispatch({ type: ACTIONS.TOGGLE_ITEM, payload: id }),
63 clearItems: () => dispatch({ type: ACTIONS.CLEAR_ITEMS })
64 };
65
66 return (
67 <TodoContext.Provider value={{ state, actions }}>
68 {children}
69 </TodoContext.Provider>
70 );
71};
72
73// Custom hook for using context
74export const useTodos = () => {
75 const context = useContext(TodoContext);
76 if (!context) {
77 throw new Error('useTodos must be used within a TodoProvider');
78 }
79 return context;
80};
81
82// Components using the provider
83const TodoList = () => {
84 const { state, actions } = useTodos();
85
86 return (
87 <div>
88 <h2>Todo List ({state.items.length})</h2>
89 {state.items.map(item => (
90 <div key={item.id} className="todo-item">
91 <span
92 className={item.completed ? 'completed' : ''}
93 onClick={() => actions.toggleItem(item.id)}
94 >
95 {item.text}
96 </span>
97 <button onClick={() => actions.removeItem(item.id)}>
98 Delete
99 </button>
100 </div>
101 ))}
102 <button onClick={actions.clearItems}>Clear All</button>
103 </div>
104 );
105};
106
107const TodoInput = () => {
108 const [text, setText] = useState('');
109 const { actions } = useTodos();
110
111 const handleSubmit = (e) => {
112 e.preventDefault();
113 if (text.trim()) {
114 actions.addItem(text);
115 setText('');
116 }
117 };
118
119 return (
120 <form onSubmit={handleSubmit}>
121 <input
122 value={text}
123 onChange={(e) => setText(e.target.value)}
124 placeholder="Add a todo..."
125 />
126 <button type="submit">Add</button>
127 </form>
128 );
129};
130
131// App with Provider
132function App() {
133 return (
134 <TodoProvider>
135 <div className="app">
136 <h1>Todo App</h1>
137 <TodoInput />
138 <TodoList />
139 </div>
140 </TodoProvider>
141 );
142}
Performance

Memoization Patterns

Memoization is a crucial optimization technique in React that prevents expensive recalculations and unnecessary re-renders. Using React.memo, useMemo, and useCallback strategically can dramatically improve your application's performance, especially with large lists, complex calculations, or frequently updating components. However, premature optimization can make code harder to read and maintain - use these patterns when you have identified actual performance bottlenecks through profiling.

Benefits

  • Prevents unnecessary re-renders
  • Improves application performance
  • Reduces computation overhead
  • Better user experience

Use Cases

Expensive calculations, large lists, complex components

Implementation

1// React.memo for component memoization
2const ExpensiveComponent = React.memo(({ data, onUpdate }) => {
3 console.log('ExpensiveComponent rendered');
4
5 return (
6 <div>
7 <h3>Expensive Component</h3>
8 <p>Processing ${data.length} items</p>
9 <button onClick={onUpdate}>Update</button>
10 </div>
11 );
12});
13
14// Custom comparison function
15const ExpensiveComponentWithCustomComparison = React.memo(
16 ({ data, metadata }) => {
17 return (
18 <div>
19 <h3>{metadata.title}</h3>
20 <p>Items: {data.length}</p>
21 </div>
22 );
23 },
24 (prevProps, nextProps) => {
25 // Custom comparison logic
26 return (
27 prevProps.data.length === nextProps.data.length &&
28 prevProps.metadata.title === nextProps.metadata.title
29 );
30 }
31);
32
33// useMemo for expensive calculations
34const DataProcessor = ({ items, filter, sortBy }) => {
35 const processedItems = useMemo(() => {
36 console.log('Processing items...');
37
38 let filtered = items.filter(item =>
39 item.category === filter || filter === 'all'
40 );
41
42 let sorted = filtered.sort((a, b) => {
43 if (sortBy === 'name') return a.name.localeCompare(b.name);
44 if (sortBy === 'date') return new Date(b.date) - new Date(a.date);
45 return 0;
46 });
47
48 return sorted;
49 }, [items, filter, sortBy]);
50
51 const stats = useMemo(() => {
52 console.log('Calculating stats...');
53 return {
54 total: processedItems.length,
55 categories: new Set(processedItems.map(item => item.category)).size,
56 averagePrice: processedItems.reduce((sum, item) => sum + item.price, 0) / processedItems.length
57 };
58 }, [processedItems]);
59
60 return (
61 <div>
62 <div className="stats">
63 <p>Total: {stats.total}</p>
64 <p>Categories: {stats.categories}</p>
65 <p>Average Price: ${stats.averagePrice.toFixed(2)}</p>
66 </div>
67
68 <div className="items">
69 {processedItems.map(item => (
70 <div key={item.id}>
71 <h4>{item.name}</h4>
72 <p>${item.price}</p>
73 </div>
74 ))}
75 </div>
76 </div>
77 );
78};
79
80// useCallback for function memoization
81const ParentComponent = () => {
82 const [count, setCount] = useState(0);
83 const [items, setItems] = useState([]);
84
85 // Without useCallback - new function on every render
86 const handleAddItem = () => {
87 setItems([...items, { id: Date.now(), name: New Item $}{Date.now()} }]);
88 };
89
90 // With useCallback - memoized function
91 const handleAddItemMemoized = useCallback(() => {
92 setItems(prevItems => [...prevItems, {
93 id: Date.now(),
94 name: New Item $}{Date.now()}
95 }]);
96 }, []); // Empty dependency array
97
98 const handleRemoveItem = useCallback((id) => {
99 setItems(prevItems => prevItems.filter(item => item.id !== id));
100 }, []); // Empty dependency array
101
102 return (
103 <div>
104 <p>Count: {count}</p>
105 <button onClick={() => setCount(count + 1)}>Increment</button>
106
107 <ItemList
108 items={items}
109 onAddItem={handleAddItemMemoized}
110 onRemoveItem={handleRemoveItem}
111 />
112 </div>
113 );
114};
115
116const ItemList = React.memo(({ items, onAddItem, onRemoveItem }) => {
117 console.log('ItemList rendered');
118
119 return (
120 <div>
121 <button onClick={onAddItem}>Add Item</button>
122 {items.map(item => (
123 <div key={item.id}>
124 <span>{item.name}</span>
125 <button onClick={() => onRemoveItem(item.id)}>Remove</button>
126 </div>
127 ))}
128 </div>
129 );
130});
Performance

Code Splitting and Lazy Loading

Code splitting is essential for building performant React applications at scale. By breaking your application into smaller chunks and loading them on demand, you can significantly reduce initial bundle size and improve time-to-interactive. React's lazy() and Suspense make this pattern easy to implement for route-based splitting, component-level splitting, or even conditional feature loading. This pattern is crucial for applications with large dependencies or features that aren't immediately needed by all users.

Benefits

  • Faster initial page load
  • Better performance on slower devices
  • Reduced bundle size
  • Load components on demand

Use Cases

Large applications, route-based splitting, conditional components

Implementation

1// Route-based code splitting
2import React, { Suspense, lazy } from 'react';
3import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
4
5// Lazy load components
6const Home = lazy(() => import('./components/Home'));
7const About = lazy(() => import('./components/About'));
8const Dashboard = lazy(() => import('./components/Dashboard'));
9const UserProfile = lazy(() => import('./components/UserProfile'));
10
11// Loading component
12const LoadingSpinner = () => (
13 <div className="loading-spinner">
14 <div className="spinner"></div>
15 <p>Loading...</p>
16 </div>
17);
18
19function App() {
20 return (
21 <Router>
22 <div className="app">
23 <nav>
24 <Link to="/">Home</Link>
25 <Link to="/about">About</Link>
26 <Link to="/dashboard">Dashboard</Link>
27 <Link to="/profile">Profile</Link>
28 </nav>
29
30 <main>
31 <Suspense fallback={<LoadingSpinner />}>
32 <Routes>
33 <Route path="/" element={<Home />} />
34 <Route path="/about" element={<About />} />
35 <Route path="/dashboard" element={<Dashboard />} />
36 <Route path="/profile" element={<UserProfile />} />
37 </Routes>
38 </Suspense>
39 </main>
40 </div>
41 </Router>
42 );
43}
44
45// Component-based lazy loading
46const LazyModal = lazy(() => import('./Modal'));
47
48const UserDashboard = () => {
49 const [showModal, setShowModal] = useState(false);
50
51 return (
52 <div>
53 <h1>Dashboard</h1>
54 <button onClick={() => setShowModal(true)}>
55 Open Modal
56 </button>
57
58 {showModal && (
59 <Suspense fallback={<div>Loading modal...</div>}>
60 <LazyModal onClose={() => setShowModal(false)} />
61 </Suspense>
62 )}
63 </div>
64 );
65};
66
67// Dynamic imports with error handling
68const useLazyComponent = (importFunc) => {
69 const [Component, setComponent] = useState(null);
70 const [loading, setLoading] = useState(false);
71 const [error, setError] = useState(null);
72
73 const loadComponent = useCallback(async () => {
74 setLoading(true);
75 setError(null);
76
77 try {
78 const module = await importFunc();
79 setComponent(() => module.default);
80 } catch (err) {
81 setError(err);
82 } finally {
83 setLoading(false);
84 }
85 }, [importFunc]);
86
87 return { Component, loading, error, loadComponent };
88};
89
90// Usage of custom hook
91const ConditionalComponent = () => {
92 const { Component, loading, error, loadComponent } = useLazyComponent(
93 () => import('./HeavyComponent')
94 );
95
96 if (error) {
97 return <div>Error loading component: {error.message}</div>;
98 }
99
100 if (loading) {
101 return <div>Loading component...</div>;
102 }
103
104 return (
105 <div>
106 {Component ? (
107 <Component />
108 ) : (
109 <button onClick={loadComponent}>Load Heavy Component</button>
110 )}
111 </div>
112 );
113};
114
115// Preloading for better UX
116const PreloadableComponent = () => {
117 const [showComponent, setShowComponent] = useState(false);
118
119 // Preload component on hover
120 const handleMouseEnter = () => {
121 import('./ExpensiveComponent');
122 };
123
124 return (
125 <div>
126 <button
127 onMouseEnter={handleMouseEnter}
128 onClick={() => setShowComponent(true)}
129 >
130 Show Component
131 </button>
132
133 {showComponent && (
134 <Suspense fallback={<div>Loading...</div>}>
135 <LazyExpensiveComponent />
136 </Suspense>
137 )}
138 </div>
139 );
140};
141
142const LazyExpensiveComponent = lazy(() => import('./ExpensiveComponent'));
Error Handling

Error Boundary Pattern

Error boundaries are React's way of handling JavaScript errors gracefully without crashing the entire application. They act as a safety net, catching errors in their child component tree and displaying a fallback UI instead of a white screen. This pattern is essential for production applications, providing better user experience and making debugging easier. While error boundaries don't catch errors in event handlers, async code, or during SSR, they're invaluable for creating resilient React applications that degrade gracefully.

Benefits

  • Prevents app crashes
  • Better user experience
  • Centralized error handling
  • Easier debugging and monitoring

Use Cases

Error logging, graceful degradation, fallback UI, debugging

Implementation

1// Error Boundary Class Component
2class ErrorBoundary extends React.Component {
3 constructor(props) {
4 super(props);
5 this.state = {
6 hasError: false,
7 error: null,
8 errorInfo: null
9 };
10 }
11
12 static getDerivedStateFromError(error) {
13 // Update state to show fallback UI
14 return { hasError: true };
15 }
16
17 componentDidCatch(error, errorInfo) {
18 // Log error to error reporting service
19 console.error('Error caught by boundary:', error, errorInfo);
20
21 this.setState({
22 error: error,
23 errorInfo: errorInfo
24 });
25
26 // Send to error reporting service
27 // logErrorToService(error, errorInfo);
28 }
29
30 render() {
31 if (this.state.hasError) {
32 // Fallback UI
33 return (
34 <div className="error-boundary">
35 <h2>Something went wrong</h2>
36 <details style={{ whiteSpace: 'pre-wrap' }}>
37 <summary>Error Details</summary>
38 <p>{this.state.error && this.state.error.toString()}</p>
39 <p>{this.state.errorInfo.componentStack}</p>
40 </details>
41 <button onClick={() => window.location.reload()}>
42 Refresh Page
43 </button>
44 </div>
45 );
46 }
47
48 return this.props.children;
49 }
50}
51
52// Hook-based Error Boundary (using react-error-boundary)
53import { ErrorBoundary } from 'react-error-boundary';
54
55function ErrorFallback({ error, resetErrorBoundary }) {
56 return (
57 <div role="alert" className="error-fallback">
58 <h2>Oops! Something went wrong</h2>
59 <p>Error: {error.message}</p>
60 <button onClick={resetErrorBoundary}>Try again</button>
61 </div>
62 );
63}
64
65// Custom hook for error handling
66const useErrorHandler = () => {
67 const [error, setError] = useState(null);
68
69 const resetError = () => setError(null);
70
71 const handleError = useCallback((error) => {
72 setError(error);
73 // Log to error service
74 console.error('Error handled:', error);
75 }, []);
76
77 useEffect(() => {
78 if (error) {
79 throw error;
80 }
81 }, [error]);
82
83 return { handleError, resetError };
84};
85
86// Usage examples
87function App() {
88 return (
89 <ErrorBoundary
90 FallbackComponent={ErrorFallback}
91 onError={(error, errorInfo) => {
92 // Log to error service
93 console.error('Global error:', error, errorInfo);
94 }}
95 onReset={() => {
96 // Clear any state or reload data
97 window.location.reload();
98 }}
99 >
100 <Header />
101 <main>
102 <Routes>
103 <Route path="/" element={<Home />} />
104 <Route path="/dashboard" element={
105 <ErrorBoundary FallbackComponent={DashboardErrorFallback}>
106 <Dashboard />
107 </ErrorBoundary>
108 } />
109 </Routes>
110 </main>
111 </ErrorBoundary>
112 );
113}
114
115// Component that might throw errors
116const ProblematicComponent = () => {
117 const [count, setCount] = useState(0);
118 const { handleError } = useErrorHandler();
119
120 const handleAsyncError = async () => {
121 try {
122 const response = await fetch('/api/data');
123 if (!response.ok) {
124 throw new Error(Failed to fetch data: $}{response.status});
125 }
126 const data = await response.json();
127 // Handle data
128 } catch (error) {
129 handleError(error);
130 }
131 };
132
133 const handleSyncError = () => {
134 if (count > 5) {
135 throw new Error('Count is too high!');
136 }
137 setCount(count + 1);
138 };
139
140 return (
141 <div>
142 <p>Count: {count}</p>
143 <button onClick={handleSyncError}>Increment</button>
144 <button onClick={handleAsyncError}>Fetch Data</button>
145 </div>
146 );
147};

General Best Practices

📝 Development

  • • Use TypeScript for better type safety
  • • Keep components small and focused
  • • Use meaningful component and prop names
  • • Implement proper error boundaries
  • • Write tests for critical components

🚀 Performance

  • • Optimize re-renders with React.memo
  • • Use lazy loading for large components
  • • Implement proper dependency arrays
  • • Avoid creating objects in render
  • • Use the React DevTools Profiler

Advertisement Space - bottom-patterns

Google AdSense: horizontal