React Hooks — Complete Guide (useState, useEffect, useContext, useReducer)
In this tutorial, you'll learn about React Hooks. We cover key concepts, practical examples, and best practices.
What You'll Learn
Master all React hooks — useState for state, useEffect for side effects, useContext for global data, useReducer for complex state, and performance hooks.
Why It Matters
Hooks are the foundation of modern React. They replaced class components and make code cleaner, more reusable, and easier to test.
Real-World Use
Managing form state (useState), fetching API data (useEffect), sharing theme across components (useContext), or handling complex state like a shopping cart (useReducer).
useState — Local State
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
const [name, setName] = useState("");
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
<input value={name} onChange={e => setName(e.target.value)} />
<p>Name: {name}</p>
</div>
);
}
Rules:
- Call
useStateat the top level of your component - Don't call hooks inside loops, conditions, or nested functions
- State updates are asynchronous
useEffect — Side Effects
import { useState, useEffect } from "react";
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchUser() {
setLoading(true);
const response = await fetch(
`https://api.example.com/users/${userId}`
);
const data = await response.json();
setUser(data);
setLoading(false);
}
fetchUser();
}, [userId]); // Re-run when userId changes
if (loading) return <p>Loading...</p>;
return <h1>{user.name}</h1>;
}
Dependency array patterns:
useEffect(fn)— runs after every renderuseEffect(fn, [])— runs once on mountuseEffect(fn, [x, y])— runs when x or y change
useContext — Global State
import { createContext, useContext, useState } from "react";
// Step 1: Create context
const ThemeContext = createContext("light");
// Step 2: Provide value
function App() {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Toolbar />
</ThemeContext.Provider>
);
}
// Step 3: Consume anywhere
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button
onClick={() =>
setTheme(theme === "light" ? "dark" : "light")
}
style={{
background: theme === "dark" ? "#333" : "#fff",
color: theme === "dark" ? "#fff" : "#333",
}}
>
Toggle Theme (current: {theme})
</button>
);
}
useReducer — Complex State
import { useReducer } from "react";
// Reducer: (state, action) => newState
function cartReducer(state, action) {
switch (action.type) {
case "ADD_ITEM":
return [...state, action.item];
case "REMOVE_ITEM":
return state.filter(item => item.id !== action.id);
case "CLEAR":
return [];
case "UPDATE_QUANTITY":
return state.map(item =>
item.id === action.id
? { ...item, quantity: action.quantity }
: item
);
default:
return state;
}
}
function ShoppingCart() {
const [cart, dispatch] = useReducer(cartReducer, []);
function addItem() {
dispatch({
type: "ADD_ITEM",
item: {
id: Date.now(),
name: "New Item",
quantity: 1,
price: 9.99,
},
});
}
return (
<div>
<button onClick={addItem}>Add Item</button>
<button onClick={() => dispatch({ type: "CLEAR" })}>
Clear Cart
</button>
<ul>
{cart.map(item => (
<li key={item.id}>
{item.name} × {item.quantity}
<button
onClick={() =>
dispatch({
type: "REMOVE_ITEM",
id: item.id,
})
}
>
Remove
</button>
</li>
))}
</ul>
</div>
);
}
useRef — Mutable References
import { useRef } from "react";
function AutoFocus() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={handleClick}>Focus Input</button>
</div>
);
}
Use cases: DOM access, storing interval IDs, keeping mutable values that don't trigger re-render.
useMemo — Memoized Values
import { useMemo } from "react";
function ExpensiveCalculation({ data }) {
const sortedData = useMemo(() => {
return [...data].sort((a, b) => b.score - a.score);
}, [data]);
return (
<ul>
{sortedData.map(item => (
<li key={item.id}>{item.name}: {item.score}</li>
))}
</ul>
);
}
useCallback — Memoized Functions
import { useCallback } from "react";
function Parent() {
const [count, setCount] = useState(0);
// Only re-created if count changes
const increment = useCallback(() => {
setCount(prev => prev + 1);
}, []);
return <Child onIncrement={increment} />;
}
Hooks Quick Reference
| Hook | Purpose |
|---|---|
useState |
Local component state |
useEffect |
Side effects (fetch, subscriptions) |
useContext |
Read from context |
useReducer |
Complex state logic |
useRef |
Mutable references, DOM access |
useMemo |
Memoize computed values |
useCallback |
Memoize function references |
useImperativeHandle |
Customize ref expose |
useLayoutEffect |
Synchronous DOM mutations |
useDebugValue |
Custom hook debug labels |
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro