When to use useEffect vs a Click Handler
User Question: So given useEffect is for side effects, if I want to load some json from an endpoint when a button is clicked, can I do that in the click handler or should I use a useEffect?
Answer:
Good question π
It depends on when you want the fetch to happen:
πΉ Using useEffect
- Use this when you want data to load automatically as a side-effect of rendering (e.g., when the component first mounts or when some dependency changes).
- Example: load movies from an API when the component is displayed.
import { useEffect, useState } from "react";
function Movies() {
const [movies, setMovies] = useState([]);
useEffect(() => {
fetch("/api/movies")
.then(res => res.json())
.then(data => setMovies(data));
}, []); // empty array β run once on mount
return <div>{movies.length} movies loaded</div>;
}
Here useEffect is right, because fetching is a side-effect of rendering.
πΉ Using a Click Handler
- Use this when you only want the fetch to happen in direct response to user action (like clicking a button).
- You donβt need useEffect at all β just call fetch in the handler.
import { useState } from "react";
function MovieLoader() {
const [movies, setMovies] = useState([]);
const handleClick = () => {
fetch("/api/movies")
.then(res => res.json())
.then(data => setMovies(data));
};
return (
<>
<button onClick={handleClick}>Load Movies</button>
<div>{movies.length} movies loaded</div>
</>
);
}
Here, useEffect would be wrong, because the fetch isnβt tied to rendering β itβs tied to the button click.
β Rule of thumb:
- If the fetch happens because the component exists or state/props changed β useEffect.
- If the fetch happens because the user explicitly did something β event handler.
Explaining res and data
User Question: Explain res and data here
const handleClick = () => {
fetch("/api/movies")
.then(res => res.json())
.then(data => setMovies(data));
};
Answer:
fetch("/api/movies")β makes an HTTP request and returns a Promise that resolves to a Response object.-
resβ the HTTP Response object. It has:res.okβ true if status 200β299res.statusβ numeric status coderes.headersβ response headers- methods like
.json(),.text(),.blob()
res.json()β reads the body and parses it as JSON. Returns a Promise resolving to JS data.dataβ the parsed JSON you actually use (e.g., an array of movies).
β So in plain English:
res= the HTTP response wrapper (status, headers, body, etc.).res.json()= extracts and parses the body as JSON.data= the actual parsed JSON you care about.
Example Article Snippet Using then()
Click the button to fetch /api/movies. Here, res is the HTTP Response (status, headers, body reader), and data is the parsed JSON from res.json().
<button id="load-movies">Load Movies</button>
<div id="status"></div>
<ul id="movie-list"></ul>
<script>
const btn = document.getElementById('load-movies');
const statusEl = document.getElementById('status');
const listEl = document.getElementById('movie-list');
btn.addEventListener('click', () => {
statusEl.textContent = 'Loading...';
fetch('/api/movies', { headers: { 'Accept': 'application/json' } })
.then(res => {
if (!res.ok) {
throw new Error(`HTTP ${res.status} ${res.statusText}`);
}
return res.json();
})
.then(data => {
listEl.innerHTML = Array.isArray(data)
? data.map(m => `<li>${m.title ?? '(untitled)'}</li>`).join('')
: `<li>Unexpected payload: ${JSON.stringify(data)}</li>`;
statusEl.textContent = Array.isArray(data)
? `${data.length} movies loaded`
: 'Loaded (non-array response)';
})
.catch(err => {
statusEl.textContent = `Failed: ${err.message}`;
});
});
</script>

















