Skip to content

Improve search stability: debounce input and cancel in-flight GitHub requests#338

Closed
ABHImaybeJEET wants to merge 5 commits into
GitMetricsLab:mainfrom
ABHImaybeJEET:single-commit/debounce
Closed

Improve search stability: debounce input and cancel in-flight GitHub requests#338
ABHImaybeJEET wants to merge 5 commits into
GitMetricsLab:mainfrom
ABHImaybeJEET:single-commit/debounce

Conversation

@ABHImaybeJEET
Copy link
Copy Markdown
Contributor

@ABHImaybeJEET ABHImaybeJEET commented May 19, 2026

THIS PR CLOSES #337 .

Description

  • Debounces search fetches to reduce redundant API calls and UI flicker.
  • Cancels previous in-flight requests using AbortController so only the latest result updates the UI.
  • Wires AbortSignal into Octokit requests (request: { signal }).
  • Adds cleanup on unmount to clear timers and abort pending requests.
  • Removed a strict minimum-query-length guard per request to avoid overly aggressive filtering.

Changes

  • Modified useGitHubData.ts:
    • Added a 350ms debounce around fetchData.
    • Added AbortController cancellation for previous requests.
    • Passed signal into octokit.request calls for both issues and commits endpoints.
    • Added cleanup effect to clear debounce timers and abort controllers on unmount.
    • Removed MIN_QUERY_LENGTH guard (now fetches are only gated by presence of octokit).

Why

  • Prevents multiple overlapping API requests when typing rapidly.
  • Reduces GitHub API consumption and the likelihood of hitting rate limits.
  • Avoids stale/out-of-order responses updating the UI.
  • Improves responsiveness and UX on slower networks.

Testing

  • Manual: type different GitHub usernames rapidly in the tracker UI and confirm:
    • Only one request is active after typing stops (350ms).
    • UI updates correspond to the latest query.
    • No flicker from out-of-order responses.
  • Optional: adjust debounce timeout (DEBOUNCE_MS) if you prefer shorter/longer delay.

Summary by CodeRabbit

  • New Features

    • Tracker adds a Commits tab with commit listing, message-first title links, repository shortcuts, and commit-specific status/importance display.
    • Commits receive automatic category and importance classification with visible badges and importance pills.
    • Filtering and counts now include commits; date display shows "N/A" for missing/invalid dates.
  • Bug Fixes

    • Improved handling of missing data, more accurate counts, and more resilient filtering and status rendering.

Review Change Stack

@netlify
Copy link
Copy Markdown

netlify Bot commented May 19, 2026

Deploy Preview for github-spy failed.

Name Link
🔨 Latest commit 06f571e
🔍 Latest deploy log https://app.netlify.com/projects/github-spy/deploys/6a11e43e877216000862b941

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2991b2e5-fb70-4ebe-ab3f-7b4d46b6c569

📥 Commits

Reviewing files that changed from the base of the PR and between e2d6ec3 and 06f571e.

📒 Files selected for processing (3)
  • src/hooks/useGitHubData.ts
  • src/pages/Tracker/Tracker.tsx
  • src/utils/commitClassifier.ts

📝 Walkthrough

Walkthrough

Adds commit classification and commits as a new data source, refactors the GitHub data hook to debounce and cancel concurrent fetches, and extends the Tracker UI with a "Commits" tab that shows commit messages, classification badges, and importance-based filtering.

Changes

Commit Tracking Feature

Layer / File(s) Summary
Commit Classification Utility
src/utils/commitClassifier.ts
Defines CommitImportance and CommitCategory types, a ClassifiedCommit shape, and classifyCommit() that infers category from commit message keywords, computes a score from change-size and keyword modifiers, and maps the score to an importance value.
GitHub Data Hook — state & fetch helpers
src/hooks/useGitHubData.ts
Adds commits/totalCommits state and helper fetchers: a simplified fetchPaginated for issues/PRs and fetchCommitsPaginated that queries search/commits, applies the preview accept header, normalizes dates, and enriches commits via classifyCommit().
GitHub Data Hook — debounced fetch, cancellation, cleanup
src/hooks/useGitHubData.ts
Rewrites fetchData into a debounced, abortable callback that concurrently requests issues/PRs/commits, aborts prior in-flight requests with AbortController, treats commit-fetch failures as empty while still setting issues/PRs, maps HTTP/message errors (sets rateLimited on 403), clears debounce timers and aborts on unmount, and extends the hook return to include commits/totalCommits.
Tracker Page UI Extension for Commits
src/pages/Tracker/Tracker.tsx
Adds a "Commits" tab, extends GitHubItem with optional commit, repository, and classifiedInfo, consumes commits/totalCommits, adds commitFilter state, updates filtering to match commit messages and repository URLs and importance, and updates table headers, icons, title link rendering, repository display, and status/importance badges for commits.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant useGitHubData
  participant Debouncer
  participant AbortController
  participant GitHubAPI
  participant Classifier as classifyCommit

  Client->>useGitHubData: fetchData(username, page, perPage)
  useGitHubData->>Debouncer: schedule debounced call
  Debouncer->>Debouncer: wait debounce interval
  alt prior request exists
    useGitHubData->>AbortController: abort()
    AbortController->>GitHubAPI: cancel pending requests
  end
  Debouncer->>GitHubAPI: search/issues (author query)
  Debouncer->>GitHubAPI: search/issues (prs author query)
  Debouncer->>GitHubAPI: search/commits (author query with preview header)
  GitHubAPI-->>useGitHubData: issues, prs, commits
  useGitHubData->>Classifier: classifyCommit(commit.message, files, adds, dels)
  Classifier-->>useGitHubData: classifiedInfo
  useGitHubData->>useGitHubData: set issues/prs/commits and totals
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

level:advanced, type:feature, quality:clean

🐰 I sniff the diff and hop with glee,
Debounced requests keep the API free.
Commits classified, badges in sight,
Tracker hums tidy through day and night.
Little paws clap — data looks just right.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes changes to Tracker.tsx and commitClassifier.ts which add commit support and filtering features beyond the original issue #337 scope of request debouncing and cancellation. Clarify whether commit support and commit classification are part of issue #337 or a separate scope, and ensure all changes align with the stated PR objectives.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main changes: debouncing input and cancelling in-flight GitHub requests to improve search stability, which matches the PR's core objectives.
Description check ✅ Passed The PR description follows the template structure with related issue, description, changes, reasoning, and testing sections. All required information is present and detailed.
Linked Issues check ✅ Passed The PR implements all core objectives from issue #337: debouncing (350ms), AbortController cancellation, AbortSignal integration, cleanup on unmount, and removal of MIN_QUERY_LENGTH guard.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/hooks/useGitHubData.ts

Parsing error: Declaration or statement expected.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pages/Tracker/Tracker.tsx (1)

97-102: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Stop refetching all datasets on tab switches.

Since fetchData now loads all tabs’ data in one call, keeping tab in this effect causes avoidable API churn.

Suggested fix
   useEffect(() => {
     if (username) {
       fetchData(username, page + 1, ROWS_PER_PAGE);
     }
-  }, [tab, page]);
+  }, [page]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/Tracker/Tracker.tsx` around lines 97 - 102, The effect is
refetching everything on tab changes even though fetchData now loads all tabs;
update the useEffect that calls fetchData(username, page + 1, ROWS_PER_PAGE) to
remove tab from its dependency list so it only runs when username or page
changes (e.g., change the dependency array to [username, page]), keeping the
existing username check inside the effect.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/hooks/useGitHubData.ts`:
- Around line 205-208: In the finally block of the fetch flow in
useGitHubData.ts, guard the setLoading(false) so a stale aborted request cannot
clear a newer active loading state: only call setLoading(false) (and null-out
abortControllerRef.current) if abortControllerRef.current === controller;
otherwise leave loading unchanged. Update the finally block that currently
references abortControllerRef, controller and setLoading to perform this
equality check before mutating state.
- Around line 4-8: The file has duplicated and misplaced code: move the import
"useState, useCallback, useRef" to the top of the module, remove the
stray/duplicate export block so there is exactly one export const useGitHubData,
delete the earlier incomplete fetchPaginated declaration and keep only the
coherent fetchPaginated implementation, and remove the legacy stray async (...)
=> { ... } block inside fetchData so that fetchData is a single valid
useCallback function; reference the exports/useGitHubData, fetchPaginated, and
fetchData symbols while making these edits.

In `@src/pages/Tracker/Tracker.tsx`:
- Around line 136-138: The importance filter currently only checks for
["High","Medium","Low"] so selecting "Unknown" in the UI has no effect; update
the predicate in the Tracker component where filterType is applied (the block
that sets filtered = filtered.filter(...)) to include "Unknown" in the allowed
list OR normalize classifiedInfo?.importance to treat undefined/null as
"Unknown" before comparing so that classifiedInfo?.importance === filterType
will match "Unknown"; make the same change in the second occurrence of this
logic (the other filtered filter block referenced around lines 356-363) to keep
behavior consistent.

---

Outside diff comments:
In `@src/pages/Tracker/Tracker.tsx`:
- Around line 97-102: The effect is refetching everything on tab changes even
though fetchData now loads all tabs; update the useEffect that calls
fetchData(username, page + 1, ROWS_PER_PAGE) to remove tab from its dependency
list so it only runs when username or page changes (e.g., change the dependency
array to [username, page]), keeping the existing username check inside the
effect.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2f0b66df-e421-46d7-91c9-1ea3bdb35b07

📥 Commits

Reviewing files that changed from the base of the PR and between 9d34c19 and c4d017d.

📒 Files selected for processing (3)
  • src/hooks/useGitHubData.ts
  • src/pages/Tracker/Tracker.tsx
  • src/utils/commitClassifier.ts

Comment thread src/hooks/useGitHubData.ts
Comment on lines +205 to +208
} finally {
setLoading(false);
if (abortControllerRef.current === controller) abortControllerRef.current = null;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard setLoading(false) so stale aborted requests don’t clear active loading state.

Only the currently active controller should finalize loading state.

Suggested fix
         } finally {
-          setLoading(false);
-          if (abortControllerRef.current === controller) abortControllerRef.current = null;
+          if (abortControllerRef.current === controller) {
+            setLoading(false);
+            abortControllerRef.current = null;
+          }
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} finally {
setLoading(false);
if (abortControllerRef.current === controller) abortControllerRef.current = null;
}
} finally {
if (abortControllerRef.current === controller) {
setLoading(false);
abortControllerRef.current = null;
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/useGitHubData.ts` around lines 205 - 208, In the finally block of
the fetch flow in useGitHubData.ts, guard the setLoading(false) so a stale
aborted request cannot clear a newer active loading state: only call
setLoading(false) (and null-out abortControllerRef.current) if
abortControllerRef.current === controller; otherwise leave loading unchanged.
Update the finally block that currently references abortControllerRef,
controller and setLoading to perform this equality check before mutating state.

Comment on lines +136 to +138
if (["High", "Medium", "Low"].includes(filterType)) {
filtered = filtered.filter(item => item.classifiedInfo?.importance === filterType);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Include Unknown in the importance filter predicate.

The UI offers Unknown, but filtering logic currently ignores it.

Suggested fix
-    if (["High", "Medium", "Low"].includes(filterType)) {
+    if (["High", "Medium", "Low", "Unknown"].includes(filterType)) {
       filtered = filtered.filter(item => item.classifiedInfo?.importance === filterType);
     }

Also applies to: 356-363

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/Tracker/Tracker.tsx` around lines 136 - 138, The importance filter
currently only checks for ["High","Medium","Low"] so selecting "Unknown" in the
UI has no effect; update the predicate in the Tracker component where filterType
is applied (the block that sets filtered = filtered.filter(...)) to include
"Unknown" in the allowed list OR normalize classifiedInfo?.importance to treat
undefined/null as "Unknown" before comparing so that classifiedInfo?.importance
=== filterType will match "Unknown"; make the same change in the second
occurrence of this logic (the other filtered filter block referenced around
lines 356-363) to keep behavior consistent.

@ABHImaybeJEET
Copy link
Copy Markdown
Contributor Author

ABHImaybeJEET commented May 23, 2026 via email

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 23, 2026

@ABHImaybeJEET Sure! I'll kick off a fresh review of the current state of your PR, taking into account all the recent changes across useGitHubData.ts, Tracker.tsx, and commitClassifier.ts. Hang tight!

(◕‿◕)っ 🐇

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/hooks/useGitHubData.ts (1)

115-119: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't turn commit API failures into a fake "0 commits" state.

Any non-abort error here is converted to { items: [], total: 0 }. The tracker renders that exactly like a real empty result set, so users can't tell whether the author has no commits or the commits endpoint failed. Bubble the error or track a partial-failure state instead of silently swallowing it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/useGitHubData.ts` around lines 115 - 119, The current catch on
fetchCommitsPaginated in useGitHubData.ts swallows all non-abort errors and
returns { items: [], total: 0 }, which hides real API failures; change the
handler so that only AbortError returns the empty fallback and all other errors
are propagated (re-throw) or converted into a distinct error/partial-failure
signal that your hook can surface (e.g., set an error state or a partialFailure
flag) instead of returning a fake zero-commit result; update the call site
around fetchCommitsPaginated(...) to either throw the caught error for upstream
handling or return a clearly labelled failure object so the UI can distinguish
"no commits" from "fetch failed."
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/hooks/useGitHubData.ts`:
- Around line 76-77: Short-circuit when the trimmed username is empty to avoid
constructing an invalid GitHub query: after computing trimmedUsername in the
function (the anonymous async fetch/search function that uses trimmedUsername
and builds q = `author:`), return early (or reset hook state) if trimmedUsername
=== "" so you never set q = "author:" or call the GitHub search; update the
logic around trimmedUsername and q to bail out before building or executing the
request and ensure the hook state is cleared/reset appropriately.
- Around line 80-90: Move cancellation/cleanup before returning when octokit is
falsy: clear any pending debounce timeout (e.g., debounceTimeoutRef.current) and
abort/reset abortControllerRef.current before the early return in the !octokit
branch so no queued callback or in-flight request can mutate state. Also,
instead of resolving getOctokit() earlier, defer calling/awaiting getOctokit()
inside the delayed debounce callback immediately before making the request so
the callback uses a fresh client. Update the logic around octokit,
abortControllerRef.current, and getOctokit() accordingly.

---

Outside diff comments:
In `@src/hooks/useGitHubData.ts`:
- Around line 115-119: The current catch on fetchCommitsPaginated in
useGitHubData.ts swallows all non-abort errors and returns { items: [], total: 0
}, which hides real API failures; change the handler so that only AbortError
returns the empty fallback and all other errors are propagated (re-throw) or
converted into a distinct error/partial-failure signal that your hook can
surface (e.g., set an error state or a partialFailure flag) instead of returning
a fake zero-commit result; update the call site around
fetchCommitsPaginated(...) to either throw the caught error for upstream
handling or return a clearly labelled failure object so the UI can distinguish
"no commits" from "fetch failed."
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0fc6fbf0-ed15-49fa-abe1-3c32325adfe4

📥 Commits

Reviewing files that changed from the base of the PR and between c4d017d and e2d6ec3.

📒 Files selected for processing (1)
  • src/hooks/useGitHubData.ts

Comment thread src/hooks/useGitHubData.ts Outdated
Comment thread src/hooks/useGitHubData.ts Outdated
@mehul-m-prajapati
Copy link
Copy Markdown
Collaborator

why state is blank ?
Capture

signal?: AbortSignal
) => {
let q = `author:${username} is:${type}`;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did you removed this code block? @ABHImaybeJEET

@ABHImaybeJEET
Copy link
Copy Markdown
Contributor Author

ABHImaybeJEET commented May 23, 2026 via email

@ABHImaybeJEET ABHImaybeJEET force-pushed the single-commit/debounce branch from e2d6ec3 to 3b33d5c Compare May 23, 2026 17:24
@ABHImaybeJEET
Copy link
Copy Markdown
Contributor Author

Hello @mehul-m-prajapati
I have tried to solve those issues with previous updates, but it has become very confusing to do it locally for now with regular commits from other sides .If you feel the code is useful ,you could merge and solve some issues, as solving locally would be a bit easier for you .In my earlier request all checks passed but there was some inherent errors, I would suggest if you feel the work valuable you can merge that and make few changes if necessary.

@ABHImaybeJEET
Copy link
Copy Markdown
Contributor Author

Otherwise, worst case, I would fork newly and again solve this issue, if thats what you feel is needed.

@mehul-m-prajapati
Copy link
Copy Markdown
Collaborator

Get latest commit and make new PR

@ABHImaybeJEET ABHImaybeJEET closed this by deleting the head repository May 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve Search Request Handling to Prevent Excessive GitHub API Calls During Rapid Input

2 participants