Skip to content

fix(perf): debounce search input to prevent excessive filter re-renders#1990

Open
anshul23102 wants to merge 1 commit into
S3DFX-CYBER:mainfrom
anshul23102:fix/debounce-search-input-1970
Open

fix(perf): debounce search input to prevent excessive filter re-renders#1990
anshul23102 wants to merge 1 commit into
S3DFX-CYBER:mainfrom
anshul23102:fix/debounce-search-input-1970

Conversation

@anshul23102

@anshul23102 anshul23102 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Description

The organization search input and mentor search input triggered a full filter/render operation on every single keystroke without debouncing, causing 15+ synchronous filter operations when typing a typical search query, resulting in UI sluggishness and unnecessary DOM thrashing.

Root Cause

The input event listeners for searchInput and mentorSearchInput called applyFilters / renderMentorFinder directly with no debounce wrapper.

Fix

  • Added a small debounce(fn, delay) utility function.
  • Wrapped the searchInput listener (and the synced hero-search input) with a 300ms debounced call to applyFilters.
  • Wrapped the mentorSearchInput listener with a 300ms debounced call to renderMentorFinder.

Testing

  • Verified with node --check that the modified file has valid syntax.
  • Manually confirmed typing in the search box no longer triggers a filter operation on every keystroke; filtering now runs only after a brief pause in typing.
  • No change to filter behavior/output, only to timing of invocation.

Fixes #1970

Review in cubic

@anshul23102
anshul23102 requested a review from S3DFX-CYBER as a code owner July 1, 2026 21:36
@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

@anshul23102 is attempting to deploy a commit to the s3dfx-cyber's projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

👋 Thanks for opening a PR, @anshul23102!

Your PR has entered the 🚦 PR Review Pipeline.

Standard PR detected — your PR will follow the standard repository review pipeline.


🔄 Review Flow

Stage Reviewer Purpose
Stage 1 🤖 Automation Validation · Duplicate Detection · AI/Slop Checks · Formatting · PR Analysis
Stage 2 👥 Repository Reviewer Code Review · Scope Validation · Quality Check
Stage 3 🔑 Project Admin / Maintainer Final Approval & Merge Decision

The automated PR analysis system will verify issue linkage, PR relevance, and contribution quality.

A pipeline status comment may appear automatically as your PR progresses.


✅ Contributor Checklist

  • Sign commits using git commit -s
  • Link a valid issue (Closes #123)
  • Keep changes focused and relevant
  • Do not include unrelated modifications
  • Ensure workflows/build/tests are passing
  • Read the appropriate contributor guide:

⚠️ Important Notes

  • Low-quality, spammy, or AI-generated PRs may be closed
  • PRs without linked issues may fail automated checks
  • Large unrelated PRs are likely to be rejected
  • Review times may vary depending on mentor/reviewer availability

Happy contributing 🚀

This message is posted automatically and only once.

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@anshul23102, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9fc4f366-4e7e-48c8-a47f-9220552c4681

📥 Commits

Reviewing files that changed from the base of the PR and between 468af0c and a3f2894.

📒 Files selected for processing (1)
  • src/js/app.js
✨ 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.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

💬 Faster Reviews & Assignments

Hi @anshul23102, for faster coordination and smoother communication, consider joining our Discord community:

👉 https://discord.gg/MmZGG2ee

Useful Channels

  • #issue-links-for-assignment → Share issue links for assignment help
  • #pr-links-for-review → Share PR links for mentor/maintainer review

Please avoid spamming channels or repeatedly pinging mentors/maintainers.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

⚠️ PR Validation Issues

Hi @anshul23102, your PR requires fixes before review.

Warnings

  • ⚠️ Missing contribution program declaration (GSSOC or NSOC).
  • ⚠️ Missing PR template section: related issue
  • ⚠️ Missing PR template section: type of change
  • ⚠️ Missing PR template section: checklist

Please push fixes after updating the PR.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

✅ DCO Sign-off Verified

Hi @anshul23102 👋

All commits in this PR contain valid Signed-off-by lines.

Thank you for following the DCO requirements 🚀

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🚦 PR Review Pipeline

Standard PR

Stage Status
Stage 1 — Automated Checks ✅ All automated checks passed
Stage 2 — Mentor/Reviewer ⏳ Awaiting reviewer approval
Stage 3 — Maintainer 🔒 Blocked until Stage 2 passes

No active issues


Last updated: Wed, 01 Jul 2026 21:38:07 GMT

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🤖 TENET Agent Review

📋 Summary

This pull request introduces a debounce utility function to src/js/app.js and applies it to the input event listeners for search fields (searchInput and mentorSearchInput). The change aims to prevent excessive re-rendering and UI sluggishness by delaying filter operations until a brief pause in user typing, thereby improving client-side performance. The approach is sound and addresses a common performance bottleneck in interactive UIs.

🔐 Security Findings

No security issues found.

🧹 Code Quality

  • src/js/app.js - Utility Function Placement: The debounce utility is added directly into a large, monolithic app.js file. For better modularity and maintainability, consider extracting general utility functions into a separate utils.js file and importing them where needed.
  • src/js/app.js - Global Scope Pollution: The debounce function is added to the global scope. While functional, encapsulating utilities within a module or an IIFE (if not using ES modules) is generally preferred to avoid polluting the global namespace.

✅ What's Done Well

  • Effective Performance Improvement: The PR correctly identifies and resolves a significant UI performance issue using a standard and proven debouncing technique.
  • Clear Problem and Solution: The PR description clearly articulates the root cause of the performance problem and how the proposed solution addresses it.
  • Standard Utility Implementation: The debounce function is correctly implemented following common patterns for this utility.

📝 Overall Verdict

[APPROVE] - The PR provides a correct and effective performance fix with no security concerns.

The org search input and mentor search input triggered a full filter/render
operation on every keystroke, causing 15+ synchronous filter operations when
typing a typical query. This adds a debounce(fn, delay) utility and wraps
both the org search and mentor search filter calls with a 300ms debounce,
so filtering only runs once the user pauses typing.

Fixes S3DFX-CYBER#1970

Signed-off-by: Anshul Jain <anshul23102@iiitd.ac.in>
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🤖 TENET Agent Review

📋 Summary

This pull request introduces a debounce utility function to src/js/app.js and applies it to the input event listeners for search fields (searchInput, mentorSearchInput, and hero-search). The change aims to prevent excessive re-rendering and UI sluggishness caused by filter operations triggering on every keystroke. The approach is sound, effectively addressing a performance bottleneck by delaying function execution until a pause in user input.

🔐 Security Findings

No security issues found.

🧹 Code Quality

  • src/js/app.js - The debounce utility function is added directly into app.js. For better modularity and reusability, especially as the project grows, consider placing general utility functions like debounce in a dedicated utils.js file and importing them where needed.

✅ What's Done Well

  • Performance Improvement: Effectively addresses a clear performance issue by preventing unnecessary DOM updates and filter re-renders.
  • Clean Implementation: The debounce utility function is well-implemented, concise, and correctly handles this context and arguments.
  • Clear Description: The PR description thoroughly explains the problem, root cause, fix, and testing steps.

📝 Overall Verdict

[APPROVE] - The PR provides a correct and effective solution to a performance issue with good code quality.

@sonarqubecloud

sonarqubecloud Bot commented Jul 1, 2026

Copy link
Copy Markdown

@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a standard trailing-edge debounce utility and wraps the searchInput, hero-search, and mentorSearchInput input event listeners with 300ms debounced versions of applyFilters and renderMentorFinder, reducing unnecessary filter re-renders on each keystroke.

  • The debounce implementation is correct and the two debounced instances (debouncedApplyFilters, debouncedRenderMentorFinder) are properly scoped within DOMContentLoaded and shared across the related listeners that need them.
  • categoryFilter, complexityFilter, sortSelect, and mentorChannelFilter are intentionally left un-debounced since they emit a single change event per interaction — this is the right choice.
  • In the hero-search sync handler, scrollIntoView still fires immediately on every keystroke while applyFilters is now delayed, causing the page to scroll to a stale list on each character; consider moving the scroll into the debounced call so it only fires when the results actually update.

Confidence Score: 4/5

Safe to merge; the change is isolated to event listener wiring and introduces no behavioral regressions in filter or mentor logic.

The debounce utility is correct and the shared timer instance between searchInput and hero-search is intentional and works as expected. The only rough edge is that scrollIntoView in the hero-search handler now fires on every keystroke while applyFilters is delayed, creating a visible desync between page scroll and result update.

src/js/app.js — specifically the hero-search input handler around line 2533

Important Files Changed

Filename Overview
src/js/app.js Adds a standard debounce utility and wraps searchInput, hero-search, and mentorSearchInput input listeners with 300ms debounced versions; straightforward and correct, with a minor UX desync between the immediate scrollIntoView call and the now-delayed applyFilters in the hero-search handler

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant U as User
    participant HI as hero-search input
    participant SI as searchInput
    participant D as debounce timer (300ms)
    participant AF as applyFilters()
    participant MI as mentorSearchInput
    participant D2 as debounce timer (300ms)
    participant RF as renderMentorFinder()

    U->>HI: keypress
    HI->>SI: "searchInput.value = e.target.value"
    HI->>HI: scrollIntoView() (immediate)
    HI->>D: debouncedApplyFilters()
    Note over D: timer reset

    U->>HI: keypress (within 300ms)
    HI->>SI: "searchInput.value = e.target.value"
    HI->>HI: scrollIntoView() (immediate)
    HI->>D: debouncedApplyFilters()
    Note over D: timer reset again

    Note over D: 300ms of inactivity
    D->>AF: fn.apply(this, args)
    AF-->>U: DOM updated with filtered results

    U->>MI: keypress
    MI->>D2: debouncedRenderMentorFinder()
    Note over D2: 300ms of inactivity
    D2->>RF: fn.apply(this, args)
    RF-->>U: Mentor list updated
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant U as User
    participant HI as hero-search input
    participant SI as searchInput
    participant D as debounce timer (300ms)
    participant AF as applyFilters()
    participant MI as mentorSearchInput
    participant D2 as debounce timer (300ms)
    participant RF as renderMentorFinder()

    U->>HI: keypress
    HI->>SI: "searchInput.value = e.target.value"
    HI->>HI: scrollIntoView() (immediate)
    HI->>D: debouncedApplyFilters()
    Note over D: timer reset

    U->>HI: keypress (within 300ms)
    HI->>SI: "searchInput.value = e.target.value"
    HI->>HI: scrollIntoView() (immediate)
    HI->>D: debouncedApplyFilters()
    Note over D: timer reset again

    Note over D: 300ms of inactivity
    D->>AF: fn.apply(this, args)
    AF-->>U: DOM updated with filtered results

    U->>MI: keypress
    MI->>D2: debouncedRenderMentorFinder()
    Note over D2: 300ms of inactivity
    D2->>RF: fn.apply(this, args)
    RF-->>U: Mentor list updated
Loading

Comments Outside Diff (1)

  1. src/js/app.js, line 2533-2541 (link)

    P2 The scrollIntoView call fires on every keystroke immediately, while applyFilters is now delayed by 300ms. This means the page scrolls to the (stale) results section on each character typed, then results update 300ms after the user stops typing. Moving the scroll inside the debounced call keeps the scroll and the render in sync, so the page only jumps when the list actually changes.

Reviews (1): Last reviewed commit: "fix(perf): debounce search input to prev..." | Re-trigger Greptile

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

1 issue found and verified against the latest diff

Confidence score: 4/5

  • In src/js/app.js, the debounce flow is only partially applied, so controls that still call applyFilters()/renderMentorFinder directly can race with a pending debounced call and trigger redundant or out-of-order renders, which can undermine the PR’s performance goal and occasionally show briefly inconsistent results — route all filter-entry paths through one debounced/cancel-aware handler (or cancel pending work before direct calls) before merging.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/js/app.js">

<violation number="1" location="src/js/app.js:2482">
P2: In a performance-focused PR aiming to prevent excessive re-renders, the debounce setup here leaves a gap: other filter controls call `applyFilters()` (or `renderMentorFinder`) directly without canceling a pending debounced search timer first. Because these functions read fresh DOM state on every call, the delayed invocation will re-process the same already-rendered state and trigger a redundant full filter/render cycle.

Consider making the debounce cancelable and clearing the pending timer before synchronous filter invocations, or having `applyFilters` short-circuit when no input state has actually changed.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/js/app.js

// Wire up filter event listeners
document.getElementById('searchInput')?.addEventListener('input', applyFilters);
const debouncedApplyFilters = debounce(applyFilters, 300);

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.

P2: In a performance-focused PR aiming to prevent excessive re-renders, the debounce setup here leaves a gap: other filter controls call applyFilters() (or renderMentorFinder) directly without canceling a pending debounced search timer first. Because these functions read fresh DOM state on every call, the delayed invocation will re-process the same already-rendered state and trigger a redundant full filter/render cycle.

Consider making the debounce cancelable and clearing the pending timer before synchronous filter invocations, or having applyFilters short-circuit when no input state has actually changed.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/js/app.js, line 2482:

<comment>In a performance-focused PR aiming to prevent excessive re-renders, the debounce setup here leaves a gap: other filter controls call `applyFilters()` (or `renderMentorFinder`) directly without canceling a pending debounced search timer first. Because these functions read fresh DOM state on every call, the delayed invocation will re-process the same already-rendered state and trigger a redundant full filter/render cycle.

Consider making the debounce cancelable and clearing the pending timer before synchronous filter invocations, or having `applyFilters` short-circuit when no input state has actually changed.</comment>

<file context>
@@ -2463,11 +2479,13 @@ document.addEventListener('DOMContentLoaded', () => {
 
   // Wire up filter event listeners
-  document.getElementById('searchInput')?.addEventListener('input', applyFilters);
+  const debouncedApplyFilters = debounce(applyFilters, 300);
+  document.getElementById('searchInput')?.addEventListener('input', debouncedApplyFilters);
   document.getElementById('categoryFilter')?.addEventListener('change', applyFilters);
</file context>

@anshul23102

Copy link
Copy Markdown
Contributor Author

Hi @S3DFX-CYBER, following up on this PR - it addresses the search debounce issue (#1970) and has been open for a while awaiting review. Could you please take a look when you get a chance, and apply the appropriate GSoC labels if it looks good? Thanks for your time.

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.

[PERFORMANCE] Search input triggers filter on every keystroke without debouncing

1 participant