Skip to content

Commit f85b32a

Browse files
committed
implement debounced city search to optimize API calls
1 parent 914fc89 commit f85b32a

File tree

1 file changed

+29
-31
lines changed

1 file changed

+29
-31
lines changed

src/pages/Weather.jsx

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* - [x] Persist last searched city (localStorage)
1515
* - [x] Add error retry button component
1616
* - [ ] Add favorites list (pin cities)
17+
* - [x] Optimize API usage by adding debounced search delay
1718
* Advanced:
1819
* - [ ] Hourly forecast visualization (line / area chart)
1920
* - [x] Animate background transitions
@@ -65,7 +66,10 @@ function renderWeatherAnimation(variant) {
6566
<>
6667
<svg className="cloud-svg cloud--left" viewBox="0 0 220 80" aria-hidden>
6768
<g filter="url(#cloudBlur)">
68-
<path className="cloud-shape" d="M20 50 C20 34 42 22 62 26 C70 16 92 12 110 22 C130 8 160 12 170 28 C196 30 206 44 190 54 L30 60 C22 60 20 54 20 50 Z" />
69+
<path
70+
className="cloud-shape"
71+
d="M20 50 C20 34 42 22 62 26 C70 16 92 12 110 22 C130 8 160 12 170 28 C196 30 206 44 190 54 L30 60 C22 60 20 54 20 50 Z"
72+
/>
6973
</g>
7074
<defs>
7175
<filter id="cloudBlur" x="-20%" y="-20%" width="140%" height="140%">
@@ -105,7 +109,7 @@ function renderWeatherAnimation(variant) {
105109
left: `${(i / 12) * 100}%`,
106110
animationDelay: `${(i % 6) * 0.4}s`,
107111
"--dur": `${10 + (i % 6)}s`,
108-
"--drift": `${(i % 2 === 0 ? -40 : 40)}px`,
112+
"--drift": `${i % 2 === 0 ? -40 : 40}px`,
109113
width: `${8 + (i % 3) * 4}px`,
110114
height: `${8 + (i % 3) * 4}px`,
111115
}}
@@ -145,10 +149,20 @@ export default function Weather() {
145149
const [activeBg, setActiveBg] = useState("default");
146150
const [prevBg, setPrevBg] = useState(null);
147151

152+
// Fetch data initially
148153
useEffect(() => {
149154
fetchWeather(city);
150155
}, []);
151156

157+
// ✅ Debounced search effect
158+
useEffect(() => {
159+
if (!city.trim()) return;
160+
const handler = setTimeout(() => {
161+
fetchWeather(city);
162+
}, 800); // delay in ms
163+
return () => clearTimeout(handler);
164+
}, [city]);
165+
152166
async function fetchWeather(c) {
153167
try {
154168
setLoading(true);
@@ -256,10 +270,7 @@ export default function Weather() {
256270

257271
{loading && <Loading />}
258272
{error && (
259-
<ErrorMessage
260-
message={error.message}
261-
onRetry={() => fetchWeather(city)}
262-
/>
273+
<ErrorMessage message={error.message} onRetry={() => fetchWeather(city)} />
263274
)}
264275

265276
{data && !loading && (
@@ -290,36 +301,23 @@ export default function Weather() {
290301

291302
{/* 3-Day Forecast */}
292303
{forecast.map((day, i) => {
293-
const condition =
294-
day.hourly?.[0]?.weatherDesc?.[0]?.value || "Clear";
304+
const condition = day.hourly?.[0]?.weatherDesc?.[0]?.value || "Clear";
295305
const badge = getBadgeStyle(condition);
296306

297307
return (
298308
<Card key={i} title={i === 0 ? "Today" : `Day ${i + 1}`}>
299-
{day.hourly?.[0] &&
300-
getIconUrl(day.hourly?.[0]?.weatherIconUrl) && (
301-
<div style={{ marginTop: 8 }}>
302-
<img
303-
src={getIconUrl(day.hourly?.[0]?.weatherIconUrl)}
304-
alt={
305-
day.hourly?.[0]?.weatherDesc?.[0]?.value ||
306-
"forecast icon"
307-
}
308-
style={{ width: 40, height: 40, objectFit: "contain" }}
309-
onError={(e) =>
310-
(e.currentTarget.style.display = "none")
311-
}
312-
/>
313-
</div>
314-
)}
309+
{day.hourly?.[0] && getIconUrl(day.hourly?.[0]?.weatherIconUrl) && (
310+
<div style={{ marginTop: 8 }}>
311+
<img
312+
src={getIconUrl(day.hourly?.[0]?.weatherIconUrl)}
313+
alt={day.hourly?.[0]?.weatherDesc?.[0]?.value || "forecast icon"}
314+
style={{ width: 40, height: 40, objectFit: "contain" }}
315+
onError={(e) => (e.currentTarget.style.display = "none")}
316+
/>
317+
</div>
318+
)}
315319

316-
<div
317-
style={{
318-
display: "flex",
319-
gap: "8px",
320-
marginTop: "17px",
321-
}}
322-
>
320+
<div style={{ display: "flex", gap: "8px", marginTop: "17px" }}>
323321
<strong>Avg Temp:</strong>{" "}
324322
{displayTemp(Number(day.avgtempC))}°{unit}
325323
<div

0 commit comments

Comments
 (0)