Skip to content

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

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

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

@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
    • GitHub commits are now tracked and displayed in the Tracker page alongside issues and pull requests
    • Commits display automatically assigned importance classifications (High, Medium, Low, Unknown)
    • Enhanced repository information display for tracked items
    • Improved date handling with fallback values

Review Change Stack

@netlify
Copy link
Copy Markdown

netlify Bot commented May 19, 2026

Deploy Preview for github-spy failed.

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

📝 Walkthrough

Walkthrough

The PR extends the GitHub tracker to support a third "Commits" tab by introducing a commit classification utility, refactoring the data hook to debounce and concurrently fetch commits with issues and PRs, and updating the tracker page UI to render and filter commit data alongside existing items.

Changes

Commit Tracking Feature

Layer / File(s) Summary
Commit Classification Utility
src/utils/commitClassifier.ts
Defines CommitImportance (High/Medium/Low/Unknown) and CommitCategory (Feature/Bugfix/Refactor/Chore/Docs/Test/Unknown) types, and exports classifyCommit() function that derives category from message keywords, scores by change size and impact modifiers, and maps the score to an importance level.
GitHub Data Hook Refactoring
src/hooks/useGitHubData.ts
Refactors useGitHubData to debounce and serialize fetch requests, cancel prior in-flight calls with AbortController, concurrently fetch issues/PRs/commits from GitHub API, classify each commit via the new utility, and update all data state in a single flow. Loosens the getOctokit parameter type, extends the return object with commits and totalCommits, and simplifies the fetchData signature to accept only (username, page, perPage).
Tracker Page UI Integration
src/pages/Tracker/Tracker.tsx
Extends the page to display and filter commits by adding a "Commits" tab alongside Issues and PRs, updates the GitHubItem model to include optional commit.message, repository.html_url, and classifiedInfo fields, expands filtering logic to support importance-based filtering for commits, displays commit-specific icons in the status column, renders the first line of commit messages in the title column, shows an importance category badge when classification data exists, and updates table headers to reflect the new commit content.

Sequence Diagram(s)

sequenceDiagram
  participant Component
  participant Hook as useGitHubData
  participant Debounce as Debouncer
  participant Controller as AbortController
  participant API as GitHub API
  
  Component->>Hook: fetchData(username, page, perPage)
  Hook->>Debounce: schedule debounced call
  Debounce->>Debounce: wait 300-500ms
  alt prior request in flight
    Hook->>Controller: abort()
  end
  Controller->>API: cancel pending request
  Debounce->>API: search issues, PRs, commits concurrently
  API-->>Hook: issues, prs, commits results
  Hook->>Hook: classify commits via classifyCommit()
  Hook->>Hook: update issues, prs, commits, totals, error, rateLimited
Loading
flowchart LR
  subgraph TabSelection["Tab Selection"]
    direction LR
    Issues["Issues Tab"]
    PRs["PRs Tab"]
    Commits["Commits Tab"]
  end
  
  subgraph DataSource["Data Source"]
    direction LR
    IssuesData["issues / totalIssues"]
    PRsData["prs / totalPrs"]
    CommitsData["commits / totalCommits"]
  end
  
  subgraph FilterType["Filter Options"]
    direction LR
    StateFilter["All/Open/Closed/Merged"]
    ImportanceFilter["All/High/Medium/Low/Unknown"]
  end
  
  subgraph Rendering["Row Rendering"]
    direction LR
    Title["First line of message or title"]
    Status["Importance pill or state badge"]
    Icon["GitCommitIcon or StateIcon"]
  end
  
  Issues --> IssuesData
  PRs --> PRsData
  Commits --> CommitsData
  
  Issues --> StateFilter
  PRs --> StateFilter
  Commits --> ImportanceFilter
  
  IssuesData --> Rendering
  PRsData --> Rendering
  CommitsData --> Rendering
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • GitMetricsLab/github_tracker#149: Introduces the foundational fetchData/pagination/concurrent-fetch pattern in useGitHubData that this PR extends with debounce and commit data.
  • GitMetricsLab/github_tracker#238: Modifies rate-limiting and error-handling logic in useGitHubData, which overlaps with the refactored error/rate-limit handling in this PR's fetchData rewrite.
  • GitMetricsLab/github_tracker#255: Changes data fetching and tab/display logic in both useGitHubData and Tracker.tsx, sharing the same core hook and UI integration points.

Suggested labels

type:feature

Poem

🐰 A commit comes hopping down the page,
Classified by score and sage—
Debounce delays the frantic call,
Commits and issues, tracked in all!
With ribbons of importance bright,
The tracker shines with fresh new light. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Beyond the primary debounce/cancellation fix, the PR introduces commit classification and tracking features (new files, tab support) not directly required by issue #337, suggesting scope expansion. Clarify whether commit tracking features are part of issue #337 or represent separate enhancements. If separate, consider splitting into distinct PRs for cleaner history.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the primary changes: debouncing input and canceling in-flight GitHub requests. It directly matches the main objectives in the linked issue.
Description check ✅ Passed The PR description is comprehensive, covering the related issue, specific changes made, rationale, and testing approach. It follows the template structure with clear sections.
Linked Issues check ✅ Passed The PR implementation fully addresses issue #337: debouncing (350ms) delays requests until typing pauses, AbortController cancels previous requests, and only the latest result updates the UI.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

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

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 on lines +4 to 8
export const useGitHubData = (getOctokit: () => any) => {
const [issues, setIssues] = useState<any[]>([]);
const [prs, setPrs] = useState<any[]>([]);
const [commits, setCommits] = useState<any[]>([]);
import { useState, useCallback, useRef } from 'react';
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 | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="src/hooks/useGitHubData.ts"

echo "Checking duplicate declarations in ${FILE}..."
rg -n "export const useGitHubData|const fetchPaginated = async|^\\s*async\\s*\\(" "$FILE"

echo
echo "Expected: each declaration should appear once in final code."
echo "If multiple hits appear for the same declaration, remove the legacy block."

Repository: GitMetricsLab/github_tracker

Length of output: 553


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="src/hooks/useGitHubData.ts"

echo "---- file head (1-90) ----"
nl -ba "$FILE" | sed -n '1,90p'

echo
echo "---- middle (90-160) ----"
nl -ba "$FILE" | sed -n '90,160p'

echo
echo "---- tail (160-260) ----"
nl -ba "$FILE" | sed -n '160,260p'

Repository: GitMetricsLab/github_tracker

Length of output: 160


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="src/hooks/useGitHubData.ts"

echo "---- file head (1-90) ----"
cat -n "$FILE" | sed -n '1,90p'

echo
echo "---- middle (90-160) ----"
cat -n "$FILE" | sed -n '90,160p'

echo
echo "---- tail (160-260) ----"
cat -n "$FILE" | sed -n '160,260p'

Repository: GitMetricsLab/github_tracker

Length of output: 9642


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="src/hooks/useGitHubData.ts"

echo "---- tail (260-420) ----"
cat -n "$FILE" | sed -n '260,420p'

echo
echo "---- very end (last 120 lines) ----"
# print last 120 lines without assuming total line count
TOTAL=$(wc -l < "$FILE")
START=$(( TOTAL>120 ? TOTAL-119 : 1 ))
cat -n "$FILE" | sed -n "${START},${TOTAL}p"

Repository: GitMetricsLab/github_tracker

Length of output: 8072


Fix broken module structure in src/hooks/useGitHubData.ts (duplicate hook + stray code blocks break compilation)

  • useGitHubData is exported twice (starts at line 4 and again at line 31) without properly closing the first export.
  • An import { useState, useCallback, useRef } from 'react'; appears inside the hook body (line 8), which is invalid syntax in TS/JS modules.
  • fetchPaginated is declared twice and the earlier declaration is left in an incomplete state before the later one (lines ~43-56).
  • fetchData contains a stray legacy async (...) => { ... } block starting around line 210, corrupting the useCallback callback structure.

Remove the leftover legacy implementation so the file contains exactly one export const useGitHubData and one coherent fetchPaginated/fetchData implementation.

🧰 Tools
🪛 Biome (2.4.15)

[error] 8-8: Illegal use of an import declaration not at the top level

(parse)

🤖 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 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.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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

1 participant