Skip to content

Commit 522d89a

Browse files
Merge pull request #72 from techieadi4703/debouncing
Implement debounced city search to optimize API calls
2 parents 549589d + ef02b81 commit 522d89a

File tree

1 file changed

+29
-39
lines changed

1 file changed

+29
-39
lines changed

src/pages/Weather.jsx

Lines changed: 29 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88
* - [ ] Add loading skeleton instead of plain text
99
* - [ ] Style forecast cards with condition color badges
1010
* Medium:
11-
* - [ ] Dynamic background / gradient based on condition (sunny, rain, snow)
12-
* - [ ] Input debounced search (on stop typing)
13-
* - [ ] Persist last searched city (localStorage)
14-
* - [ ] Add error retry button component
15-
* - [ ] Add favorites list (pin cities)
11+
* - [x] Dynamic background / gradient based on condition (sunny, rain, snow)
12+
* - [x] Input debounced search (on stop typing)
13+
* - [x] Persist last searched city (localStorage)
14+
* - [x] Add error retry button component
15+
* - [ ] Add favorites list (pin cities)
16+
* - [x] Optimize API usage by adding debounced search delay
1617
* Advanced:
1718
* - [ ] Hourly forecast visualization (line / area chart)
1819
* - [ ] Animate background transitions
@@ -239,6 +240,15 @@ export default function Weather() {
239240
return null;
240241
}
241242

243+
// ✅ Debounced search effect
244+
useEffect(() => {
245+
if (!city.trim()) return;
246+
const handler = setTimeout(() => {
247+
fetchWeather(city);
248+
}, 800); // delay in ms
249+
return () => clearTimeout(handler);
250+
}, [city]);
251+
242252
async function fetchWeather(c) {
243253
try {
244254
setLoading(true);
@@ -385,10 +395,7 @@ export default function Weather() {
385395

386396
{loading && <Loading />}
387397
{error && (
388-
<ErrorMessage
389-
message={error.message}
390-
onRetry={() => fetchWeather(city)}
391-
/>
398+
<ErrorMessage message={error.message} onRetry={() => fetchWeather(city)} />
392399
)}
393400

394401
{data && !loading && (
@@ -427,40 +434,23 @@ export default function Weather() {
427434

428435
{/* 3-Day Forecast */}
429436
{forecast.map((day, i) => {
430-
const condition =
431-
day.hourly?.[0]?.weatherDesc?.[0]?.value || "Clear";
437+
const condition = day.hourly?.[0]?.weatherDesc?.[0]?.value || "Clear";
432438
const badge = getBadgeStyle(condition);
433439

434440
return (
435441
<Card key={i} title={i === 0 ? "Today" : `Day ${i + 1}`}>
436-
{day.hourly?.[0] &&
437-
getIconUrl(day.hourly?.[0]?.weatherIconUrl) && (
438-
<div style={{ marginTop: 8 }}>
439-
<img
440-
src={getIconUrl(day.hourly?.[0]?.weatherIconUrl)}
441-
alt={
442-
day.hourly?.[0]?.weatherDesc?.[0]?.value ||
443-
"forecast icon"
444-
}
445-
style={{
446-
width: 40,
447-
height: 40,
448-
objectFit: "contain",
449-
}}
450-
onError={(e) =>
451-
(e.currentTarget.style.display = "none")
452-
}
453-
/>
454-
</div>
455-
)}
456-
457-
<div
458-
style={{
459-
display: "flex",
460-
gap: "8px",
461-
marginTop: "17px",
462-
}}
463-
>
442+
{day.hourly?.[0] && getIconUrl(day.hourly?.[0]?.weatherIconUrl) && (
443+
<div style={{ marginTop: 8 }}>
444+
<img
445+
src={getIconUrl(day.hourly?.[0]?.weatherIconUrl)}
446+
alt={day.hourly?.[0]?.weatherDesc?.[0]?.value || "forecast icon"}
447+
style={{ width: 40, height: 40, objectFit: "contain" }}
448+
onError={(e) => (e.currentTarget.style.display = "none")}
449+
/>
450+
</div>
451+
)}
452+
453+
<div style={{ display: "flex", gap: "8px", marginTop: "17px" }}>
464454
<strong>Avg Temp:</strong>{" "}
465455
{displayTemp(Number(day.avgtempC))}°{unit}
466456
<div

0 commit comments

Comments
 (0)