DG
DevGuide
Run Code
Home/React/useContext, useRef, useMemo, useCallback
⚛ React4 / 4

useContext, useRef, useMemo, useCallback

📌 WHAT

Advanced hooks for sharing state globally (useContext), referencing DOM nodes (useRef), caching values (useMemo), and caching functions (useCallback).

💡 WHY

These solve specific performance and architecture problems: avoid prop drilling, access DOM directly, prevent unnecessary re-calculations and re-renders.

⏰ WHEN / WHERE

useContext: global state like auth/theme. useRef: DOM access, storing values without re-render. useMemo: expensive calculations. useCallback: stable function references for child components.

🔧 HOW

Each hook has a specific signature. useContext reads from a Provider. useRef returns a mutable .current. useMemo/useCallback take a function and dependency array.

Code Examples

#1useContext — avoid prop drilling

useContext — avoid prop drilling
tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
"use client";
import { createContext, useContext, useState } from "react";
// 1. Create the context
interface AuthContextType {
user: User | null;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
}
const AuthContext = createContext<AuthContextType | null>(null);
// 2. Create provider component
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const login = async (email: string, password: string) => {
const data = await fetch("/api/auth/login", {
method: "POST",
body: JSON.stringify({ email, password }),
}).then(r => r.json());
setUser(data.user);
};
const logout = () => setUser(null);
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
// 3. Custom hook for easy consumption
export function useAuth() {
const context = useContext(AuthContext);
if (!context) throw new Error("useAuth must be inside AuthProvider");
return context;
}
// 4. Use anywhere in the tree — no prop drilling!
function Header() {
const { user, logout } = useAuth();
return (
<header>
{user ? (
<>
<span>Welcome, {user.name}</span>
<button onClick={logout}>Logout</button>
</>
) : (
<a href="/login">Login</a>
)}
</header>
);
}

#2useRef, useMemo, useCallback

useRef, useMemo, useCallback
tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
"use client";
import { useRef, useMemo, useCallback, useState } from "react";
// useRef — 1. DOM access
function SearchInput() {
const inputRef = useRef<HTMLInputElement>(null);
const focusInput = () => {
inputRef.current?.focus(); // directly access DOM element
};
return (
<>
<input ref={inputRef} type="text" placeholder="Search..." />
<button onClick={focusInput}>Focus Search</button>
</>
);
}
// useRef — 2. Store value without triggering re-render
function Timer() {
const intervalRef = useRef<NodeJS.Timeout | null>(null);
const start = () => {
intervalRef.current = setInterval(() => console.log("tick"), 1000);
};
const stop = () => {
if (intervalRef.current) clearInterval(intervalRef.current);
};
return <><button onClick={start}>Start</button><button onClick={stop}>Stop</button></>;
}
// useMemo — cache expensive calculation
function ProductList({ products, category }: Props) {
// Only recalculates when products or category changes
const filteredProducts = useMemo(() => {
console.log("Filtering..."); // watch when this runs
return products
.filter(p => p.category === category)
.sort((a, b) => a.price - b.price);
}, [products, category]); // dependencies
return <ul>{filteredProducts.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}
// useCallback — stable function reference
function Parent() {
const [count, setCount] = useState(0);
// Without useCallback: new function created on every render
// ChildComponent would re-render every time Parent re-renders
// With useCallback: same function reference between renders
const handleItemClick = useCallback((id: number) => {
console.log("Clicked:", id);
}, []); // no dependencies = created once
return (
<>
<button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
<ChildComponent onItemClick={handleItemClick} />
</>
);
}

Practice This Concept

Open the playground, paste any example, and experiment. The best way to learn is by breaking things.

Open in Playground