React Hooks Complete Guide 2026: Master Modern React Development
Everything you need to know about React Hooks - from basics to advanced patterns. Learn useState, useEffect, useContext, and create your own custom hooks.
Full Stack Developer | React Expert
Introduction to React Hooks
React Hooks revolutionized how we write React components. Introduced in React 16.8, hooks allow you to use state and other React features in functional components, making your code more readable, reusable, and maintainable.
In this comprehensive guide, we'll explore all the built-in hooks and learn how to create custom hooks for your specific needs.
useState: Managing Component State
The useState hook is the most fundamental hook in React. It allows you to add state to functional components.
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const [name, setName] = useState('Guest');
return (
<div>
<h2>Count: {count}</h2>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter your name"
/>
</div>
);
};
export default Counter;Key useState Patterns
- •Functional updates:
setCount(prev => prev + 1) - •Lazy initialization:
useState(() => computeExpensiveValue()) - •Object state: Use spread operator for updates
useEffect: Handling Side Effects
The useEffect hook lets you perform side effects in functional components. It's equivalent to componentDidMount, componentDidUpdate, and componentWillUnmount combined.
import React, { useState, useEffect } from 'react';
const UserProfile = ({ userId }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Fetch user data
fetchUser(userId)
.then(userData => {
setUser(userData);
setLoading(false);
});
// Cleanup function
return () => {
// Cleanup code here
};
}, [userId]); // Dependency array
if (loading) return <div>Loading...</div>;
return <div>{user.name}</div>;
};Use Effect Patterns
- •Run once: Empty dependency array
[] - •Run on specific changes:
[prop, state] - •Cleanup: Return function from useEffect
useContext: Sharing Data Across Components
useContext allows you to share data between components without prop drilling. It's perfect for theme data, user authentication, and global state.
import React, { createContext, useContext } from 'react';
// Create context
const ThemeContext = createContext();
// Provider component
const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};
// Custom hook for using theme
const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
};
// Usage in component
const Header = () => {
const { theme, setTheme } = useTheme();
return (
<header className={theme}>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
</header>
);
};useReducer: Complex State Management
useReducer is an alternative to useState for complex state logic. It's especially useful when state depends on previous values or when you have multiple sub-values.
import React, { useReducer } from 'react';
// Reducer function
const counterReducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
case 'RESET':
return { count: 0 };
default:
return state;
}
};
const Counter = () => {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<div>
<h2>Count: {state.count}</h2>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>
Increment
</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>
Decrement
</button>
<button onClick={() => dispatch({ type: 'RESET' })}>
Reset
</button>
</div>
);
};Performance Optimization: useCallback & useMemo
These hooks help optimize performance by memoizing functions and values to prevent unnecessary re-renders.
import React, { useState, useCallback, useMemo } from 'react';
const ExpensiveComponent = ({ items }) => {
const [filter, setFilter] = useState('');
// Memoize expensive calculation
const filteredItems = useMemo(() => {
console.log('Filtering items...');
return items.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
}, [items, filter]);
// Memoize event handler
const handleItemClick = useCallback((item) => {
console.log('Item clicked:', item);
}, []);
return (
<div>
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="Filter items..."
/>
{filteredItems.map(item => (
<div key={item.id} onClick={() => handleItemClick(item)}>
{item.name}
</div>
))}
</div>
);
};When to Use Each
useCallback
- • Event handlers passed to child components
- • Functions used in useEffect dependencies
- • Preventing unnecessary re-renders
useMemo
- • Expensive calculations
- • Derived state
- • Reference equality checks
Creating Custom Hooks
Custom hooks are JavaScript functions whose names start with "use" and can call other hooks. They allow you to extract component logic into reusable functions.
// Custom hook for API calls
const useApi = (url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
};
// Usage in component
const UserProfile = ({ userId }) => {
const { data: user, loading, error } = useApi(`/api/users/${userId}`);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>{user.name}</div>;
};Rules of Hooks
Two Essential Rules
- 1.Only Call Hooks at the Top Level:
Don't call hooks inside loops, conditions, or nested functions.
- 2.Only Call Hooks from React Functions:
Call hooks from React functional components or custom hooks.
Conclusion
React Hooks have transformed how we write React applications. They provide a more direct API to React concepts and make code reuse and organization much easier.
By mastering these hooks and patterns, you'll be able to build more efficient, maintainable, and scalable React applications. Remember to always follow the Rules of Hooks and use TypeScript when possible for better development experience.
Keep practicing with these patterns, and soon you'll be writing clean, efficient React code like a pro!
Ready to Master React?
Check out my other React tutorials and start building amazing applications!