From 45e45328592777f1de3a2633e72ec21c1797e1ae Mon Sep 17 00:00:00 2001
From: "P. Clawmogorov" <262173731+Alm0stSurely@users.noreply.github.com>
Date: Sun, 12 Apr 2026 10:09:53 +0000
Subject: [PATCH] fix(watch-button): eliminate N+1 API calls on profile
watchlist
## Problem
The WatchButton component was firing N separate checkIsWatched() API
calls for N repos in the watchlist, even though the parent already knew
these repos were watched by definition.
## Solution
- Add optional initialIsWatched prop to skip redundant API calls
- Add optional onUnwatch callback for optimistic UI removal
- Profile page now passes initialIsWatched={true} for all watchlist items
## Result
Profile page with 10 watched repos now fires 1 API call instead of 11.
Fixes #57
---
frontend/app/(auth)/profile/page.tsx | 7 ++++++-
frontend/components/watch-button.tsx | 16 ++++++++++++----
2 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/frontend/app/(auth)/profile/page.tsx b/frontend/app/(auth)/profile/page.tsx
index 9e4f42d..1dbae77 100644
--- a/frontend/app/(auth)/profile/page.tsx
+++ b/frontend/app/(auth)/profile/page.tsx
@@ -274,7 +274,12 @@ function Watchlist() {
-
+ setRepos(prev => prev.filter(r => r.id !== repo.id))}
+ />
diff --git a/frontend/components/watch-button.tsx b/frontend/components/watch-button.tsx
index a804417..f6a38fc 100644
--- a/frontend/components/watch-button.tsx
+++ b/frontend/components/watch-button.tsx
@@ -9,21 +9,28 @@ import { toast } from "sonner";
interface WatchButtonProps {
owner: string;
name: string;
+ initialIsWatched?: boolean;
+ onUnwatch?: () => void;
}
-export function WatchButton({ owner, name }: WatchButtonProps) {
+export function WatchButton({ owner, name, initialIsWatched, onUnwatch }: WatchButtonProps) {
const { token } = useAuth();
- const [isWatched, setIsWatched] = useState(false);
- const [loading, setLoading] = useState(true);
+ const [isWatched, setIsWatched] = useState(initialIsWatched ?? false);
+ const [loading, setLoading] = useState(initialIsWatched === undefined);
useEffect(() => {
+ // Skip the API call if the parent gave us the initial state
+ if (initialIsWatched !== undefined) {
+ setLoading(false);
+ return;
+ }
if (token) {
checkIsWatched(token, owner, name)
.then(setIsWatched)
.catch(() => setIsWatched(false))
.finally(() => setLoading(false));
}
- }, [token, owner, name]);
+ }, [token, owner, name, initialIsWatched]);
const toggleWatch = async () => {
if (!token) return;
@@ -34,6 +41,7 @@ export function WatchButton({ owner, name }: WatchButtonProps) {
await removeFromWatchlist(token, owner, name);
setIsWatched(false);
toast.success("Removed from watchlist");
+ onUnwatch?.();
} else {
await addToWatchlist(token, owner, name);
setIsWatched(true);