Timers, saving data, talking to servers, touching the DOM and building your own hooks: the tools behind every real React app.
4modules
40practice examples
~17hclass time
100%live code
Keys: → next ← back M menu L Code Lab F fullscreen
M9
useEffect & Lifecycle
Side effects, dependencies, cleanup
M10
Fetching Data from APIs
fetch, loading/error, POST, TanStack Query
M11
useRef & the DOM
Focus, canvas, timers, previous values
M12
Custom Hooks
useFetch, useLocalStorage, useForm...
BrainBit Infotech, Ranchi | Trainer: Nandan Kumar
09
MODULE 9 • PART 3
useEffect & the Component Lifecycle
Some work happens outside of drawing the screen: timers, saving data, changing the tab title, listening to the keyboard. useEffect is where that code lives.
What a "side effect" is
Pure rendering (the recipe rule)
useEffect syntax
The dependency array: 3 cases (animated timeline)
Mount, update, unmount
Cleanup functions
StrictMode and the infinite-loop trap
When NOT to use an effect
YOU WILL BUILD:Tab title counterLive clockAuto-save notesExam countdownDebounced searchStopwatch with laps
The browser tab title says (3) BrainBit Infotech Portal: 3 unread messages
Talking to the outside world
Timers, the browser tab title, localStorage, keyboard and scroll events, network requests: none of these are "drawing JSX".
Effects run AFTER the screen updates
React first paints the UI, then runs your effect. The user never waits for it.
Clean up when you leave
A timer must be stopped when the component disappears, or it keeps running in the background (a "memory leak").
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Core idea
Pure rendering vs side effects
🍳 Rendering should be like a recipe
Same ingredients (props + state) → same dish (JSX), every time. A recipe does not phone your friend or set an alarm. Your component body should only calculate and return JSX.
Wrong.jsx
function Profile({ user }) {
document.title = user.name; // side effect in render: BAD
localStorage.setItem('u', user.id); // BAD
return <h1>{user.name}</h1>;
}
Side effect
Example
Timers
setInterval, setTimeout
Browser APIs
document.title, localStorage, scroll
Event listeners
window.addEventListener('keydown')
Network
fetch('/api/students')
Third-party code
maps, charts, video players
Right.jsx
function Profile({ user }) {
useEffect(() => {
document.title = user.name; // GOOD: inside an effect
}, [user.name]);
return <h1>{user.name}</h1>;
}
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Live demo
useEffect: your first effect
useEffect(fn, deps) tells React: "after you draw the screen, run fn". Here it updates the browser tab title whenever count changes. Watch the console to see when it runs.
App.jsx
LIVELive Preview
Console
✍ Try it Click twice. The console order is always: render first, effect second.
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Animated explainer
The dependency array: 3 cases
Effect written as...
Render 1 first time (mount)
Render 2 name changed
Render 3 count changed
Render 4 count changed
useEffect(fn)
✓
✓
✓
✓
useEffect(fn, [])
✓
—
—
—
useEffect(fn, [count])
✓
—
✓
✓
No array
Runs after every render. Rarely what you want.
Empty array []
Runs once after the first render. For setup: start a timer, add a listener, load data.
[count]
Runs first time + whenever count changes. List every value from props/state that the effect uses.
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Live demo
The 3 cases live
Three effects in one component. Press the buttons and read the console. Changing name does not trigger the [count] effect.
App.jsx
LIVELive Preview
Console
✍ Try it Click "Change name": only A runs. Click "Change count": A and C run. B never runs again.
Complete React Course | Part 3 | useEffect & the Component Lifecycle
🔄 UPDATE (every second)setTime → re-render. Deps [] unchanged, so the effect does NOT run again
↓
🗑️ UNMOUNTClock is removed (user leaves the page) → React runs the CLEANUP
↓
✅ Timer stoppedno ghost timers, no memory leak
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Live demo
Cleanup live: mount & unmount a timer
Click Show clock to mount it (timer starts), then Hide clock to unmount it (cleanup stops the timer). Without cleanup, hidden clocks would keep ticking forever!
App.jsx
LIVELive Preview
Console
✍ Try it Toggle the clock 3 times and read the console: every start has a matching stop.
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Common mistakes
Two traps: StrictMode & infinite loops
🔁 "My effect runs twice!"
In development, Vite wraps your app in <StrictMode>. React mounts, unmounts and mounts again on purpose to check that your cleanup works. In the production build it runs once. Do not remove StrictMode, fix the cleanup instead.
Common mistakeAn effect that sets the same state it depends on loops forever and freezes the browser. Ask: does this really need an effect? Often it should be an event handler or a calculated value.
In real projectsInstall the ESLint rule react-hooks/exhaustive-deps (already in Vite templates). It warns you about missing dependencies.
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Best practice
When NOT to use useEffect
Situation
Do NOT do this
Do this instead
Value calculated from state/props
useEffect(() => setTotal(a + b), [a, b])
const total = a + b; (derived)
Reacting to a click
set a flag, then react in an effect
Put the code directly in the onClick handler
Filtering a list
useEffect(() => setFiltered(...), [list, q])
const filtered = list.filter(...)
Resetting state when a prop changes
effect that resets state
Give the component a key so React remounts it
Fetching data in big apps
many hand-written fetch effects
TanStack Query or your framework's data loading (Module 10)
Key ideaOfficial advice from react.dev: "You Might Not Need an Effect". Effects are for syncing with things OUTSIDE React. If no outside system is involved, you probably do not need one.
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Practice 1 of 10 • Easy
Example 1: Tab Title with Unread Count
● Easy💼 Real project: Gmail, WhatsApp Web, Facebook tabs
When the unread count changes, an effect updates document.title. In a real app look at the browser tab; here we also show the title on screen so you can see it.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Make the title show "🔴" before the count when unread is more than 5.
Inside the effect: (unread > 5 ? '🔴 ' : '') + ...
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Practice 2 of 10 • Easy
Example 2: Welcome Toast that Hides Itself
● Easy💼 Real project: Notifications, "Saved!" messages
An effect with [] runs once on mount and starts a 3-second setTimeout that hides the toast. The cleanup cancels the timer if the component disappears early.
App.jsxApp.css
LIVELive Preview
Console
✍ Try it yourself Add a close button (×) inside the toast so the user can hide it early.
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Practice 3 of 10 • Easy
Example 3: Live Digital Clock
● Easy💼 Real project: Dashboards, attendance kiosks
An interval updates the time every second. It starts on mount and is cleared in the cleanup. Indian date format with toLocaleDateString('en-IN').
App.jsx
LIVELive Preview
Console
✍ Try it yourself Add a line that says "Good morning / afternoon / evening" based on now.getHours().
Reuse the logic from Module 1 Example 6.
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Practice 4 of 10 • Easy
Example 4: Auto-Save Notes
● Easy💼 Real project: Google Keep, Notion, draft emails
One effect loads the saved note on mount (actually done in the useState initializer). Another effect saves to localStorage whenever the text changes. Refresh with Re-run: your note is still there!
App.jsx
LIVELive Preview
Console
✍ Try it yourself Type a note, then press Re-run on the preview. The note comes back. Add a "Clear note" button.
<button onClick={() => setText('')}>Clear</button> (the effect saves the empty text too).
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Practice 5 of 10 • Easy
Example 5: Reading Progress Bar
● Easy💼 Real project: Blogs & news articles
We listen to the page scroll event in an effect and calculate how far the reader has scrolled. The listener is removed in the cleanup.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Show a "Back to top ↑" button only when progress is above 30%. Clicking it calls window.scrollTo(0, 0).
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Practice 6 of 10 • Medium
Example 6: Exam Countdown Timer
●● Medium💼 Real project: Online tests, OTP timers, sale countdowns
The effect depends on running. When running is true it starts an interval; when paused, the cleanup stops it. At zero we stop automatically and show a message.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Add a progress ring or bar that shrinks as time passes (width = left / START * 100%).
A div with style={{ width: (left / START) * 100 + '%' }} inside a grey track.
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Practice 7 of 10 • Medium
Example 7: Auto-Play Slider that Pauses on Hover
●● Medium💼 Real project: Homepage banners
The interval effect depends on paused. Hovering sets paused = true, so the cleanup clears the timer; leaving restarts it. Using the updater i => i + 1 avoids stale values.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Add dots under the slider; clicking a dot jumps to that banner.
Map banners to small spans with onClick={() => setI(idx)}.
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Practice 8 of 10 • Medium
Example 8: Arrow-Key Game Movement
●● Medium💼 Real project: Games, keyboard shortcuts, slideshows
A keydown listener on window moves a character on a grid. We add it in an effect and remove it in the cleanup. Click inside the preview first so it receives your key presses.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Add WASD keys as well as arrow keys.
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Practice 9 of 10 • Hard
Example 9: Debounced Search
●●● Hard💼 Real project: Search boxes that call an API (Amazon, YouTube)
We should not search on every key press. The effect waits 500 ms after the user stops typing; if they type again, the cleanup cancels the old timer. This is called debouncing. Watch the console.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Type "python" quickly: the console shows ONE search, not six. Change the delay to 1000 ms and compare.
Without the cleanup, every key press would trigger a search.
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Practice 10 of 10 • Hard
Example 10: Stopwatch with Saved Laps
●●● Hard💼 Real project: Sports apps, study timers (Pomodoro)
Combines an interval effect (depends on running), lap recording, and a second effect that persists laps to localStorage. Laps survive a refresh. Fastest and slowest laps are highlighted (derived values).
App.jsxApp.css
LIVELive Preview
Console
✍ Try it yourself Record 3 laps, then press Re-run: they are restored. Add the average lap time below the list.
lapTimes.reduce((a, b) => a + b, 0) / lapTimes.length, formatted with fmt().
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Check your understanding
Quick Quiz
Click an answer. Wrong answers shake, the right one turns green and explains why.
Q1. When does useEffect(fn, []) run?
✔ An empty dependency array means "only on mount".
Q2. What is the cleanup function for?
✔ Return a function from the effect to undo what it started.
Q3. const total = a + b vs an effect that sets total. Which is better?
✔ Derived values do not need effects.
🎯
Your score: 0 / 3 (first try counts)
Answer all 3 questions. Discuss any wrong answer with your partner before moving on.
Complete React Course | Part 3 | useEffect & the Component Lifecycle
M9Module summary
Recap: What you learned
✔Effects run AFTER render, for work outside React (timers, title, storage, listeners, network).
✔No array = every render; [] = once; [x] = when x changes.
✔Return a cleanup to stop timers and remove listeners.
✔StrictMode runs effects twice in development to test your cleanup.
✔Never set state that the same effect depends on without a condition.
✔You might not need an effect: derive values, use event handlers.
🏆
Module 9 complete!
Finish all 10 examples and the "Try it yourself" tasks before moving on.
Practice folder: D:\react-practice\m09
➡ Next up
Module 10: Fetching Data from APIs. Load real data with loading and error states.
Complete React Course | Part 3 | useEffect & the Component Lifecycle
10
MODULE 10 • PART 3
Fetching Data from APIs
Real apps get their data from servers: students from a database, weather from a weather service, products from a shop backend. Learn to load, show and send data safely.
What an API and JSON are (waiter analogy)
fetch + async/await inside useEffect
Loading, error and success states
Our classroom mock API
Race conditions & AbortController
Sending data with POST
Skeletons & retry buttons
TanStack Query & environment variables
YOU WILL BUILD:Student listWeather appProduct searchFeedback form (POST)Load-more feedCached product list
Complete React Course | Part 3 | Fetching Data from APIs
M10Practice server
Our classroom mock API (works offline)
Request
Returns
GET /api/students
8 students (id, name, class, city, marks)
GET /api/students?city=Ranchi
filtered students
GET /api/products?q=dokra
12 Jharkhand handicraft products
GET /api/products/3
one product
GET /api/weather?city=Ranchi
temp, condition, humidity, wind
GET /api/users/1
a user profile
GET /api/posts?page=2&limit=5
paged posts: { items, page, totalPages, hasMore }
GET /api/news, /api/tip, /api/quiz, /api/todos
headlines, a random tip, quiz questions, todos
GET /api/error
status 500 (to practise errors)
GET /api/slow
answers after 2.5 seconds
POST / PUT any /api/...
status 201 with your data + a new id
⏳
Realistic delay
Every request takes about half a second, so you can SEE loading states.
🏠
In your own project
Replace /api/... with a real URL, e.g. your Node/Django backend or a free test API like jsonplaceholder.typicode.com.
🔍
See real requests
In a real app open DevTools (F12) > Network tab to watch every request and response.
Complete React Course | Part 3 | Fetching Data from APIs
M10Live demo
fetch inside useEffect
Load data once when the component mounts: useEffect(..., []). fetch() returns a Promise; res.json() turns the response into JavaScript; then we save it in state.
App.jsx
LIVELive Preview
Console
✍ Try it Change the URL to /api/students?city=Ranchi. How many come back?
Complete React Course | Part 3 | Fetching Data from APIs
Complete React Course | Part 3 | Fetching Data from APIs
M10Live demo
The professional pattern: async/await + states
An effect function cannot be async itself, so we create an async function inside it and call it. res.ok is false for errors like 404/500: we throw so that catch handles it. finally always turns loading off.
App.jsx
LIVELive Preview
Console
✍ Try it Click "User 9": that user does not exist, so you see the error state (HTTP 404).
Complete React Course | Part 3 | Fetching Data from APIs
M10Animated explainer
Race conditions & AbortController
time →
type "r"request A sent
type "re"request B sent
B returns firstshows "re" results ✔
A returns LATEoverwrites with "r" results ❌
App.jsx
useEffect(() => {
const controller = new AbortController();
fetch('/api/products?q=' + q, { signal: controller.signal })
.then((r) => r.json()).then(setResults)
.catch((e) => { if (e.name !== 'AbortError') setError(e.message); });
return () => controller.abort(); // cancel the old request
}, [q]);
Key ideaWhen q changes, the cleanup aborts the previous request, so a slow old answer can never overwrite the new one.
In real projectsLibraries like TanStack Query handle this for you automatically.
Complete React Course | Part 3 | Fetching Data from APIs
M10Live demo
Sending data: POST
To create something on the server, use method: 'POST', a JSON body and a Content-Type header. The server answers with the saved object (here our mock adds an id).
App.jsx
LIVELive Preview
Console
✍ Try it Add a student and check the console: status 201 means "Created".
Complete React Course | Part 3 | Fetching Data from APIs
Do not hard-code server URLs or keys. Vite reads a .env file. Only variables starting with VITE_ reach your React code.
.env
# .env (in the project root)
VITE_API_URL=https://api.brainbit-teaching.com
App.jsx
const API = import.meta.env.VITE_API_URL;
fetch(`${API}/students`);
Common mistakeAnything in the browser can be seen by users. NEVER put secret passwords or private keys in React code or .env files of a front-end app.
Complete React Course | Part 3 | Fetching Data from APIs
M10Practice 1 of 10 • Easy
Example 1: Student List from the API
● Easy💼 Real project: School ERP student register
The simplest data fetch: on mount, GET /api/students, save the array in state, and render a table. Uses async/await inside the effect.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Sort the students by marks (highest first) before rendering.
[...students].sort((a, b) => b.marks - a.marks).map(...)
Complete React Course | Part 3 | Fetching Data from APIs
M10Practice 2 of 10 • Easy
Example 2: Loading Spinner
● Easy💼 Real project: Any screen that waits for data
A loading state starts as true. While loading we show an animated spinner; when the data arrives we turn it off. The /api/slow endpoint takes 2.5 seconds so you can see it clearly.
App.jsxApp.css
LIVELive Preview
Console
✍ Try it yourself Press Re-run to see the spinner again. Then show the number of seconds waited under the spinner.
A second effect with setInterval that counts while loading is true.
Complete React Course | Part 3 | Fetching Data from APIs
M10Practice 3 of 10 • Easy
Example 3: Error Message with Retry
● Easy💼 Real project: Handling "server down" gracefully
We call /api/error, which always fails with status 500. res.ok is false, so we throw, catch it and show a friendly error box. The Retry button increases attempt, which re-runs the effect.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Press Retry until it works (3rd attempt). Then change the condition so it works on the 2nd attempt.
Change attempt < 3 to attempt < 2.
Complete React Course | Part 3 | Fetching Data from APIs
M10Practice 4 of 10 • Easy
Example 4: User Profile by ID
● Easy💼 Real project: Profile pages (/users/42)
The effect depends on userId, so selecting another user fetches again. A small profile card shows the result; a loading flag dims the old card while the new one loads.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Show "Loading..." text next to the select while loading is true.
{loading && <span> Loading...</span>}
Complete React Course | Part 3 | Fetching Data from APIs
M10Practice 5 of 10 • Easy
Example 5: Load News on Button Click
● Easy💼 Real project: Refresh buttons, "tip of the day"
Not every fetch belongs in an effect! When data should load because of a click, call fetch directly in the event handler.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Click "Tip of the day" several times: you get a random tip each time.
Event-driven fetches do not need useEffect at all.
Complete React Course | Part 3 | Fetching Data from APIs
M10Practice 6 of 10 • Medium
Example 6: Weather App by City
●● Medium💼 Real project: Weather apps, travel planners
The user picks a city; on submit we fetch /api/weather?city=.... We handle 404 ("City not found") as an error, and show a weather card on success.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Search "Patna" to see the 404 error. Then add a "Use Celsius / Fahrenheit" toggle (convert on display).
F = C * 9 / 5 + 32. Keep the API data in Celsius; convert only when rendering.
Complete React Course | Part 3 | Fetching Data from APIs
M10Practice 7 of 10 • Medium
Example 7: Product Search with Debounce + Abort
●● Medium💼 Real project: Search-as-you-type on shopping sites
Two professional tricks together: debounce (wait 400 ms after typing) and AbortController (cancel old requests). The cleanup handles both. The console shows which requests are cancelled.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Add a category dropdown (All, Home, Art, Clothing, Jewellery, Handicraft) and send it as &category=.
Add it to the URL only when it is not "All", and add the category state to the dependency array.
Complete React Course | Part 3 | Fetching Data from APIs
M10Practice 8 of 10 • Medium
Example 8: Feedback Form (POST)
●● Medium💼 Real project: Contact forms, reviews, complaints
A controlled form that POSTs JSON to /api/feedback. We disable the button while sending, show the server response, and reset the form. Status 201 = Created.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Log the full response object in the console and point out which fields came from you and which from the server.
name/rating/message are yours; id and savedAt were added by the server.
Complete React Course | Part 3 | Fetching Data from APIs
M10Practice 9 of 10 • Hard
Example 9: Load-More Posts (Pagination)
●●● Hard💼 Real project: Social feeds, blogs, product lists
Each page comes from /api/posts?page=N&limit=5. Clicking Load more fetches the next page and appends it to the list. The API tells us hasMore, so we hide the button at the end.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Turn it into infinite scroll: load the next page automatically when the user scrolls near the bottom.
Add a scroll listener effect (like Module 9 Example 5) that calls setPage when close to the bottom and not loading.
Complete React Course | Part 3 | Fetching Data from APIs
M10Practice 10 of 10 • Hard
Example 10: TanStack Query Product List
●●● Hard💼 Real project: Production apps with caching
The industry-standard way. useQuery gives data, isPending, error and caching. Switch categories and come back: cached data appears instantly (no spinner) while it refreshes in the background.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Click Home, then Art, then Home again: the second time Home appears instantly. Change staleTime to 0 and compare.
With staleTime 0, cached data still shows instantly but it always refetches in the background.
Complete React Course | Part 3 | Fetching Data from APIs
M10Check your understanding
Quick Quiz
Click an answer. Wrong answers shake, the right one turns green and explains why.
Q1. What does res.json() do?
✔ It parses the response body (returns a Promise).
Q2. Why can a fetch result be "out of date" when typing fast?
✔ Abort old requests or ignore their results in the cleanup.
Q3. Which status code means "created" after a POST?
✔ 201 Created. 404 = not found, 500 = server error.
🎯
Your score: 0 / 3 (first try counts)
Answer all 3 questions. Discuss any wrong answer with your partner before moving on.
Complete React Course | Part 3 | Fetching Data from APIs
M10Module summary
Recap: What you learned
✔An API returns JSON; GET reads, POST creates, PUT/PATCH update, DELETE removes.
✔Fetch on mount inside useEffect(..., []) with an async inner function.
✔Always handle loading, error (res.ok) and success.
✔Abort or ignore old requests in the cleanup to avoid race conditions.
✔Fetch inside event handlers when data loads because of a click.
✔TanStack Query adds caching and retries; keep URLs in VITE_ env variables.
🏆
Module 10 complete!
Finish all 10 examples and the "Try it yourself" tasks before moving on.
Practice folder: D:\react-practice\m10
➡ Next up
Module 11: useRef & the DOM. Focus inputs, measure elements and remember values without re-rendering.
Complete React Course | Part 3 | Fetching Data from APIs
11
MODULE 11 • PART 3
useRef & Working with the DOM
Sometimes you need to touch a real DOM element (focus an input, scroll, draw on a canvas) or remember a value without redrawing. That is what refs are for.
A ref = a pocket that does not re-render
useState vs useRef (animated)
Refs to DOM elements
Focus, scroll and measure
Storing timer ids in refs
Remembering the previous value
Uncontrolled inputs
forwardRef and React 19 ref props
YOU WILL BUILD:Auto-focus searchScroll to sectionSignature padStopwatchOTP inputClick-outside dropdown
↑ cursor jumped to box 4 automatically (ref.focus())
✍️ sign here (canvas ref)
Focus management
OTP boxes that move to the next box, search bars that focus on page load, chat inputs that focus after sending.
Direct DOM work
Drawing on a canvas, playing videos, scrolling to a section, measuring an element size.
Remembering without redrawing
Timer ids, previous values, "how many times did this render": values that should not update the screen.
Complete React Course | Part 3 | useRef & Working with the DOM
M11Animated explainer
A ref is a pocket that does not re-render
📦 useState = a notice board
Change it and everyone sees: React re-renders the component.
state value
0
re-render 🔄
👝 useRef = a private pocket
You can put things in and take them out any time, but React does not redraw when it changes.
ref.current (real value) → screen shows
0 → 0
no re-render 😴
useState
useRef
Returns
[value, setValue]
{ current: value }
Changing it
setValue(x) → re-render
ref.current = x → NO re-render
Use for
anything shown on screen
DOM elements, timer ids, previous values
Complete React Course | Part 3 | useRef & Working with the DOM
M11Live demo
State vs ref: see the difference
Click "ref + 1" several times: the console shows the ref growing, but the screen does not change. Then click "state + 1": the re-render reveals the ref's real value.
App.jsx
LIVELive Preview
Console
✍ Try it Click "ref + 1" five times, then "state + 1" once. The ref line jumps to 5.
Complete React Course | Part 3 | useRef & Working with the DOM
M11Live demo
Refs to DOM elements
Pass a ref to the ref attribute of any element. After React draws it, ref.current is the real DOM node, so you can call browser methods: focus(), scrollIntoView(), select(), read its size...
App.jsx
LIVELive Preview
Console
✍ Try it Click each button. Then add a button that scrolls the box into view: boxRef.current.scrollIntoView().
Complete React Course | Part 3 | useRef & Working with the DOM
M11Rules
Rules for using refs
✅
Read/write refs in event handlers and effects
That is where the DOM exists and where changing values is safe.
❌
Do not read or write ref.current during render
The JSX would not update when it changes; it makes components unpredictable.
🚪
Refs are an escape hatch
If you can do it with props and state (show/hide, change text, colours), do that instead of touching the DOM.
⚙️
Do not change DOM that React controls
Focus, scroll, measure and play are fine. Do not delete or add elements React manages.
forwardRef.jsx
// React 18: pass a ref to YOUR component with forwardRef
const FancyInput = forwardRef(function FancyInput(props, ref) {
return <input ref={ref} className="fancy" {...props} />;
});
// React 19: ref is just a normal prop
function FancyInput({ ref, ...props }) {
return <input ref={ref} className="fancy" {...props} />;
}
// Using it (same in both versions)
const r = useRef(null);
<FancyInput ref={r} />
r.current.focus();
In real projectsOur live previews run React 18, so the examples use forwardRef. In React 19 projects you can skip it.
Complete React Course | Part 3 | useRef & Working with the DOM
M11Practice 1 of 10 • Easy
Example 1: Auto-Focus the Search Box
● Easy💼 Real project: Google search page, chat apps
When the page loads, an effect calls inputRef.current.focus(), so the user can start typing immediately. (HTML also has an autoFocus attribute, but refs let you focus at ANY time.)
App.jsx
LIVELive Preview
Console
✍ Try it yourself Also select all text when the input is focused: add onFocus={(e) => e.target.select()} and put a default value.
defaultValue="React" and the onFocus handler.
Complete React Course | Part 3 | useRef & Working with the DOM
M11Practice 2 of 10 • Easy
Example 2: Focus Input on "Edit" Click
● Easy💼 Real project: Inline editing (rename a file, edit a title)
Clicking Edit switches to edit mode with state. After React shows the input, an effect focuses it using the ref. Press Enter to save.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Press Escape to cancel editing and restore the old title.
Save the old title in a ref when editing starts; on Escape, setTitle(oldRef.current) and stop editing.
Complete React Course | Part 3 | useRef & Working with the DOM
M11Practice 3 of 10 • Easy
Example 3: Scroll to a Section
● Easy💼 Real project: Landing pages with "Contact us" buttons
Each section has its own ref. Clicking a menu button calls scrollIntoView({ behavior: 'smooth' }) on that section.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Add a "Back to top" button in the Contact section.
Add a ref to the nav (or the first section) and scroll to it.
Complete React Course | Part 3 | useRef & Working with the DOM
M11Practice 4 of 10 • Easy
Example 4: Count Renders with a Ref
● Easy💼 Real project: Debugging performance
A ref survives re-renders but changing it does not cause one, so it is perfect for counting how many times a component rendered (changing state here would loop forever!).
App.jsx
LIVELive Preview
Console
✍ Try it yourself Type 5 letters and check the console count. Why is the number on screen one behind?
The effect increases the ref AFTER the render is shown, and changing the ref does not trigger a new render.
Complete React Course | Part 3 | useRef & Working with the DOM
M11Practice 5 of 10 • Easy
Example 5: Uncontrolled Form with Refs
● Easy💼 Real project: Simple forms, file inputs, third-party widgets
Instead of state for every field, refs read the values only when the form is submitted. Simpler for small forms, and required for <input type="file">.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Choose an image file and submit. Add a "Reset" button that calls e.target.form.reset().
A <button type="reset"> also works without any code!
Complete React Course | Part 3 | useRef & Working with the DOM
M11Practice 6 of 10 • Medium
Example 6: Canvas Signature Pad
●● Medium💼 Real project: Delivery apps, digital forms, drawing apps
A canvas needs direct DOM access: the ref gives us the canvas, we get its 2D context and draw lines on mouse moves. A second ref remembers whether the mouse is down (no re-render needed).
App.jsx
LIVELive Preview
Console
✍ Try it yourself Add colour buttons (purple, black, red) that change ctx.strokeStyle.
Get the context from the ref inside the click handler and set strokeStyle.
Complete React Course | Part 3 | useRef & Working with the DOM
M11Practice 7 of 10 • Medium
Example 7: Stopwatch with an Interval Ref
●● Medium💼 Real project: Timers that start and stop on clicks
The interval id must survive re-renders but should not be shown, so it lives in a ref. Start stores the id; Stop uses it to clear the interval. (Compare with the effect version in Module 9.)
App.jsx
LIVELive Preview
Console
✍ Try it yourself Add a "Resume" button that continues from where it stopped (hint: remember the elapsed time in another ref).
On stop: elapsed.current += now - startTime; on resume start counting again and add elapsed.current to the display.
Complete React Course | Part 3 | useRef & Working with the DOM
M11Practice 8 of 10 • Medium
Example 8: Price Up / Down Indicator
●● Medium💼 Real project: Stock tickers, live cricket scores, crypto prices
A ref stores the previous price. During each update we compare the new value with prevRef.current to show ▲ or ▼, then save the new value for next time.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Keep a list of the last 10 prices in state and draw tiny bars for them (a mini chart).
setHistory(h => [...h, newPrice].slice(-10)) and map to divs with height based on the price.
Complete React Course | Part 3 | useRef & Working with the DOM
M11Practice 9 of 10 • Hard
Example 9: OTP Input Boxes with Auto-Advance
●●● Hard💼 Real project: Bank apps, UPI, phone verification
An array of refs points to 6 inputs. Typing a digit focuses the next box; Backspace on an empty box goes back; pasting a 6-digit code fills all boxes. A reusable OtpBox uses forwardRef.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Auto-focus the first box when the component mounts.
useEffect(() => refs.current[0].focus(), [])
Complete React Course | Part 3 | useRef & Working with the DOM
M11Practice 10 of 10 • Hard
Example 10: Click-Outside to Close Dropdown
●●● Hard💼 Real project: Menus, profile dropdowns, date pickers
A ref on the dropdown container lets us check if a click happened outside it: !ref.current.contains(e.target). The document listener is added only while the menu is open and removed in the cleanup. Escape closes it too.
App.jsx
LIVELive Preview
Console
✍ Try it yourself Put two Dropdowns side by side. Opening one and clicking the other should close the first.
It already works! Each Dropdown has its own ref and listener.
Complete React Course | Part 3 | useRef & Working with the DOM
M11Check your understanding
Quick Quiz
Click an answer. Wrong answers shake, the right one turns green and explains why.
Q1. Changing ref.current causes...
✔ Refs are mutable boxes; React does not watch them.
Q2. Where should you call inputRef.current.focus()?
✔ The DOM element exists only after render; use handlers or effects.
Q3. Best place to keep a setInterval id that must not be displayed?
✔ A ref survives renders without causing them, and each component instance gets its own.
🎯
Your score: 0 / 3 (first try counts)
Answer all 3 questions. Discuss any wrong answer with your partner before moving on.
Complete React Course | Part 3 | useRef & Working with the DOM
M11Module summary
Recap: What you learned
✔useRef(initial) returns { current } that survives renders.
✔Changing a ref does NOT re-render; use state for anything on screen.
✔ref={myRef} gives you the real DOM node: focus, scroll, measure, canvas.
✔Store timer ids and previous values in refs.
✔Uncontrolled inputs read values from refs on submit (file inputs need this).
✔forwardRef (React 18) / ref prop (React 19) pass refs to your components.
🏆
Module 11 complete!
Finish all 10 examples and the "Try it yourself" tasks before moving on.
Practice folder: D:\react-practice\m11
➡ Next up
Module 12: Custom Hooks. Package your logic into reusable hooks.
Complete React Course | Part 3 | useRef & Working with the DOM
12
MODULE 12 • PART 3
Custom Hooks
When the same stateful logic appears in many components, pull it out into your own hook. Custom hooks are how professional React code stays short and clean.
Why custom hooks (animated)
Anatomy of a custom hook
Each component gets its own state
Rules for custom hooks
Returning values, functions or objects
Hooks that use effects
Hooks that fetch data
Popular hook libraries
YOU WILL BUILD:useToggleuseLocalStorageuseFetchuseDebounceuseFormuseCart
import { useState, useEffect } from 'react';
// 1. Name starts with "use"
// 2. Takes any arguments you want
export function useLocalStorage(key, initialValue) {
// 3. Uses other hooks inside
const [value, setValue] = useState(() => {
const saved = localStorage.getItem(key);
return saved !== null ? JSON.parse(saved) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
// 4. Returns whatever is useful
return [value, setValue];
}
// In any component:
const [name, setName] = useLocalStorage('name', '');
🏷️
Name starts with use
useSomething. React and ESLint use the name to check the Rules of Hooks.
🧩
Just a function
A custom hook is a normal JavaScript function that calls other hooks. No magic!
📤
Return anything
An array [value, setValue] like useState, an object {{ data, loading }}, or a single value.
📁
Where to keep them
A src/hooks/ folder: useFetch.js, useDebounce.js...
Complete React Course | Part 3 | Custom Hooks
M12Live demo
Each component gets its OWN state
Custom hooks share logic, not state. Two components calling useCounter() each get a separate counter, exactly like calling useState twice.
App.jsxuseCounter.js
LIVELive Preview
Console
✍ Try it Click Post A three times: Post B stays 0. Open the useCounter.js tab to see the hook.
Complete React Course | Part 3 | Custom Hooks
M12Ecosystem
Popular hook libraries (do not reinvent everything)
Library
Gives you
Example
@tanstack/react-query
data fetching, caching, mutations
useQuery, useMutation
react-router-dom
routing hooks (Module 14)
useNavigate, useParams
react-redux
global store hooks (Module 15)
useSelector, useDispatch
react-hook-form
forms with validation
useForm
usehooks-ts / @uidotdev/usehooks
ready-made small hooks
useLocalStorage, useDebounce, useOnClickOutside
In real projectsWrite your own hooks to learn and for app-specific logic (useCart, useAuth). For common needs, a well-tested library saves time.
Key ideaHooks can call other hooks. useCart can use useLocalStorage, which uses useState + useEffect. Build big things from small hooks!
Complete React Course | Part 3 | Custom Hooks
M12Practice 1 of 10 • Easy
Example 1: useToggle
● Easy💼 Real project: Modals, dark mode, show/hide password, menus
The smallest useful hook: a boolean plus a function to flip it. One hook powers three different toggles in the same component.
App.jsxuseToggle.js
LIVELive Preview
Console
✍ Try it yourself Add a third returned value setOn so callers can force it on or off: return [on, toggle, setOn].
Then use const [x, toggleX, setX] = useToggle() and a button onClick={() => setX(false)}.
Complete React Course | Part 3 | Custom Hooks
M12Practice 2 of 10 • Easy
Example 2: useCounter with Step & Limits
● Easy💼 Real project: Quantity pickers, ratings, sliders
A configurable counter hook: start value, step, min and max. It returns an object so callers pick only what they need.
App.jsxuseCounter.js
LIVELive Preview
Console
✍ Try it yourself Add a third counter for "Guests" from 1 to 6, step 1.
One more useCounter({ start: 1, min: 1, max: 6 }) line and its buttons.
Complete React Course | Part 3 | Custom Hooks
M12Practice 3 of 10 • Easy
Example 3: useDocumentTitle
● Easy💼 Real project: Every page in a multi-page app
A tiny hook that wraps the effect from Module 9. Pages just call useDocumentTitle('Cart (3)'). The hook also restores the old title when the component unmounts.
App.jsxuseDocumentTitle.js
LIVELive Preview
Console
✍ Try it yourself Watch the console while switching pages and adding items.
The title line under the buttons shows the value from the previous render (the effect runs after render).
Complete React Course | Part 3 | Custom Hooks
M12Practice 4 of 10 • Easy
Example 4: useLocalStorage
● Easy💼 Real project: Remembering user settings, carts, drafts
Works exactly like useState, but the value is saved in localStorage and comes back after a refresh. Press Re-run after changing things.
App.jsxuseLocalStorage.js
LIVELive Preview
Console
✍ Try it yourself Change all three settings, press Re-run: everything is remembered. Add a "Reset all" button.
Call each setter with its default value.
Complete React Course | Part 3 | Custom Hooks
M12Practice 5 of 10 • Easy
Example 5: useWindowSize
● Easy💼 Real project: Responsive layouts in JavaScript (mobile vs desktop menus)
The hook listens to resize and returns width and height. The component switches between a mobile and desktop layout. Open it in the Lab and resize the browser window to see it change.
App.jsxuseWindowSize.js
LIVELive Preview
Console
✍ Try it yourself Change the breakpoint from 500 to 700 pixels and see which layout the preview uses.
The preview is about 540 px wide.
Complete React Course | Part 3 | Custom Hooks
M12Practice 6 of 10 • Medium
Example 6: useFetch Used by Two Components
●● Medium💼 Real project: Any app that loads data in many places
All the loading/error/abort logic from Module 10 lives in useFetch. Now StudentCount and WeatherBadge load data in 1 line each.
App.jsxuseFetch.js
LIVELive Preview
Console
✍ Try it yourself Add a NewsTicker component that uses useFetch('/api/news') and shows the first headline.
Three lines: call the hook, handle loading, show data[0].title.
Complete React Course | Part 3 | Custom Hooks
M12Practice 7 of 10 • Medium
Example 7: useDebounce for Search
●● Medium💼 Real project: Search boxes, auto-save, resize handlers
useDebounce(value, 500) returns the value only after it stops changing for 500 ms. The input stays instant; the expensive search uses the debounced value.
App.jsxuseDebounce.js
LIVELive Preview
Console
✍ Try it yourself Type "bamboo" quickly and count the API calls in the console. Then use the same hook to debounce a second input.
Hooks are reusable: const d2 = useDebounce(other, 300).
Complete React Course | Part 3 | Custom Hooks
M12Practice 8 of 10 • Medium
Example 8: useInterval for a Live Score
●● Medium💼 Real project: Live cricket scores, polling dashboards
useInterval(callback, delay) runs the callback every delay ms; pass null to pause. It stores the latest callback in a ref so it never uses stale state.
App.jsxuseInterval.js
LIVELive Preview
Console
✍ Try it yourself Stop the match automatically when wickets reach 10 (pass null as the delay).
useInterval(..., live && wickets < 10 ? 1000 : null)
Complete React Course | Part 3 | Custom Hooks
M12Practice 9 of 10 • Hard
Example 9: useForm with Validation Rules
●●● Hard💼 Real project: Every form in a real project
A mini version of React Hook Form: give it initial values and validation rules; get back values, errors, handleChange, handleBlur and handleSubmit. The component becomes tiny.
App.jsxuseForm.js
LIVELive Preview
Console
✍ Try it yourself Add a "phone" field with a 10-digit rule. Only App.jsx changes: the hook works for any fields!
Add phone: '' to the initial values, a rule, and {field('phone', 'Mobile')}.
Complete React Course | Part 3 | Custom Hooks
M12Practice 10 of 10 • Hard
Example 10: useCart with Persistence
●●● Hard💼 Real project: E-commerce apps
All cart logic in one hook: add, remove, change quantity, clear, plus derived count and total. It builds on useLocalStorage (a hook using a hook!), so the cart survives a refresh.
App.jsxuseCart.jsuseLocalStorage.js
LIVELive Preview
Console
✍ Try it yourself Add items, press Re-run: the cart is still there. Add a gst (18%) and grandTotal to what the hook returns.
Compute them from total inside useCart and include them in the returned object.
Complete React Course | Part 3 | Custom Hooks
M12Check your understanding
Quick Quiz
Click an answer. Wrong answers shake, the right one turns green and explains why.
Q1. A custom hook must...
✔ The use prefix lets React check the Rules of Hooks.
Q2. Two components call useCounter(). Do they share the count?
✔ Hooks share logic, not state. Use Context or a store to share state.
Q3. Can a custom hook call another custom hook?
✔ Yes! useCart can use useLocalStorage, which uses useState and useEffect.
🎯
Your score: 0 / 3 (first try counts)
Answer all 3 questions. Discuss any wrong answer with your partner before moving on.
Complete React Course | Part 3 | Custom Hooks
M12Module summary
Recap: What you learned
✔Custom hooks extract repeated stateful logic into useSomething().
✔They are normal functions that call other hooks.
✔Each component using a hook gets its own independent state.
✔Return arrays (like useState) or objects (named values).