Skip to content

feat: add debounced search for pool filtering on Explore page - #87

Open
Atharva0506 wants to merge 3 commits into
StabilityNexus:mainfrom
Atharva0506:feature/debounced-pool-search
Open

feat: add debounced search for pool filtering on Explore page#87
Atharva0506 wants to merge 3 commits into
StabilityNexus:mainfrom
Atharva0506:feature/debounced-pool-search

Conversation

@Atharva0506

@Atharva0506 Atharva0506 commented Mar 20, 2026

Copy link
Copy Markdown
Member

Addressed Issues:

Fixes #86

Screenshots/Recordings:

No visual screenshots applicable. The UI remains visually identical, but performance is optimized. The pool listing now has a 300ms rendering delay while actively typing in the search bar to prevent browser lag.

Additional Notes:

What was fixed and why:
The explorePools/page.tsx component was previously suffering from a performance bottleneck during search operations. The <PoolSearch /> component was passing the keystroke events directly to the searchQuery state, and the filteredPools useMemo block was depending directly on that raw state.
Because the useMemo block iterates over the entire pools array and applies 5 different lowercase string matching algorithms (includes) per pool, firing this evaluation synchronously on every single keystroke caused the main thread to block. On devices with lower processing power, or as the number of supported pools grows across multiple chains, this resulted in severe UI freezing and input lag.
Implementation Details:

  1. Created useDebouncedValue<T>: I added a generic, reusable React hook in src/hooks/useDebouncedValue.ts that safely delays state updates by a configured time (default 300ms) using setTimeout.
  2. Decoupled input state from filtering state: In explorePools/page.tsx, the searchQuery state is still updated instantly. This ensures the actual <input> element remains 100% responsive and never drops keystrokes.
  3. Optimized useMemo: The filteredPools array now depends on debouncedSearchQuery instead of searchQuery. This guarantees that the heavy array filtering operation only fires after the user pauses typing for 300ms, completely resolving the browser lag.

We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact. AI slop is strongly discouraged and may lead to banning and blocking. Do not spam our repos with AI slop.

Check one of the checkboxes below:

  • This PR does not contain AI-generated code at all.
  • This PR contains AI-generated code. I have read the AI Usage Policy and this PR complies with this policy. I have tested the code locally and I am responsible for it.

I have used the following AI models and tools: TODO

Checklist

  • My PR addresses a single issue, fixes a single bug or makes a single improvement.
  • My code follows the project's code style and conventions
  • If applicable, I have made corresponding changes or additions to the documentation
  • If applicable, I have made corresponding changes or additions to tests
  • My changes generate no new warnings or errors
  • I have joined the Discord server and I will share a link to this PR with the project maintainers there
  • I have read the Contribution Guidelines
  • Once I submit my PR, CodeRabbit AI will automatically review it and I will address CodeRabbit's comments.
  • I have filled this PR template completely and carefully, and I understand that my PR may be closed without review otherwise.

Summary by CodeRabbit

  • New Features
    • Improved Explore Pools search responsiveness by applying a brief delay to typed queries.
  • Bug Fixes
    • Reduced unnecessary filtering during rapid typing, helping displayed results stay aligned with the latest search input.
  • Refactor
    • Consolidated delayed search handling into a reusable approach for more consistent behavior across the app.

@coderabbitai

coderabbitai Bot commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6fa4cede-631d-4bea-a4cc-aed82ba75ffe

📥 Commits

Reviewing files that changed from the base of the PR and between 0a5ca9f and 8522f40.

📒 Files selected for processing (1)
  • src/app/explorePools/page.tsx

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


Walkthrough

This PR adds a reusable 300 ms debounce hook and applies it to pool filtering. It removes the generic useDebouncedSearch export while preserving optimized data caching, retries, cancellation, and wrapper behavior.

Changes

Search Debouncing Architecture

Layer / File(s) Summary
Optimized data fetching core
src/hooks/useOptimizedData.ts
useOptimizedData retains cache handling, staleness checks, retries, request cancellation, cleanup, callbacks, and its existing return shape.
Portfolio and pools wrapper hooks
src/hooks/useOptimizedData.ts
useOptimizedPortfolio and useOptimizedPools retain their cache keys, timing defaults, and input-based enabled gating.
Search debounce integration
src/hooks/useDebouncedValue.ts, src/app/explorePools/page.tsx, src/hooks/useOptimizedData.ts
Added generic useDebouncedValue<T> with a configurable delay and a 300 ms default. Pool filtering and PoolList now use the debounced query. Removed useDebouncedSearch.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 8522f

The PR adds 300ms-debounced pool filtering while keeping search input updates immediate. No actionable merge-blocking risk remains beyond normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant ExplorePoolsPage
  participant useDebouncedValue
  participant PoolList

  User->>ExplorePoolsPage: Enter search text
  ExplorePoolsPage->>useDebouncedValue: Pass searchQuery
  useDebouncedValue-->>ExplorePoolsPage: Return query after 300 ms
  ExplorePoolsPage->>PoolList: Pass debounced searchQuery
  ExplorePoolsPage->>ExplorePoolsPage: Recompute filteredPools
Loading

Suggested labels: Typescript Lang

Suggested reviewers: rohans02

Poem

A rabbit types, then waits with care.
The query settles in the air.
Three hundred ticks guide the flow.
The pool list filters smooth and slow.
Old search logic hops away.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding debounced search for pool filtering on the Explore page.
Linked Issues check ✅ Passed The changes satisfy issue #86. They add the reusable generic useDebouncedValue hook with a 300 ms default delay, keep search input updates immediate, apply the debounced query to pool filtering and th…
Out of Scope Changes check ✅ Passed The changes are limited to the Explore Pools filtering behavior and related hook cleanup. No unrelated code changes are identified.
Full details: Linked Issues check

Explanation

The changes satisfy issue #86. They add the reusable generic useDebouncedValue hook with a 300 ms default delay, keep search input updates immediate, apply the debounced query to pool filtering and the empty state, and remove the unused useDebouncedSearch hook.

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
src/app/explorePools/page.tsx (1)

563-576: 🧹 Nitpick | 🔵 Trivial

Compute searchLower once per memo, not once per pool.

At Line 566, debouncedSearchQuery.toLowerCase() is recalculated for every item in pools. Move it outside the filter callback to reduce repeated work on large lists.

♻️ Suggested refactor
-  const filteredPools = useMemo(
-    (): Pool[] =>
-      pools.filter((pool: Pool) => {
-        const searchLower = debouncedSearchQuery.toLowerCase();
+  const filteredPools = useMemo((): Pool[] => {
+      const searchLower = debouncedSearchQuery.toLowerCase();
+      return pools.filter((pool: Pool) => {
         const priceFeedName = getPriceFeedName(pool.priceFeedAddress, pool.chainId);
         return (
           pool.name.toLowerCase().includes(searchLower) ||
           pool.bullToken.symbol.toLowerCase().includes(searchLower) ||
           pool.bearToken.symbol.toLowerCase().includes(searchLower) ||
           pool.chainName.toLowerCase().includes(searchLower) ||
           priceFeedName.toLowerCase().includes(searchLower)
         );
-      }),
-    [pools, debouncedSearchQuery]
-  );
+      });
+    }, [pools, debouncedSearchQuery]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/explorePools/page.tsx` around lines 563 - 576, The filter currently
calls debouncedSearchQuery.toLowerCase() for every pool; compute searchLower
once outside the callback inside the useMemo to avoid repeated work: inside the
useMemo for filteredPools, create a const searchLower =
debouncedSearchQuery.toLowerCase() before pools.filter, then use that variable
within the filter callback along with existing helpers like
getPriceFeedName(pool.priceFeedAddress, pool.chainId); update the dependency
array stays [pools, debouncedSearchQuery].
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/hooks/useDebouncedValue.ts`:
- Line 1: This module uses React client hooks (useState, useEffect) in the
useDebouncedValue hook but lacks the Next.js client directive; add the exact
string "use client"; as the first line of the file (before any imports) so the
module is explicitly a client component/module and prevents server-boundary
misuse.

---

Outside diff comments:
In `@src/app/explorePools/page.tsx`:
- Around line 563-576: The filter currently calls
debouncedSearchQuery.toLowerCase() for every pool; compute searchLower once
outside the callback inside the useMemo to avoid repeated work: inside the
useMemo for filteredPools, create a const searchLower =
debouncedSearchQuery.toLowerCase() before pools.filter, then use that variable
within the filter callback along with existing helpers like
getPriceFeedName(pool.priceFeedAddress, pool.chainId); update the dependency
array stays [pools, debouncedSearchQuery].
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4b2ee4a5-d2db-463e-a51a-318415ff8057

📥 Commits

Reviewing files that changed from the base of the PR and between fbd27aa and 716123f.

📒 Files selected for processing (2)
  • src/app/explorePools/page.tsx
  • src/hooks/useDebouncedValue.ts

Comment thread src/hooks/useDebouncedValue.ts
@Atharva0506

Atharva0506 commented Mar 20, 2026

Copy link
Copy Markdown
Member Author

@blizet

there is already a useDebouncedSearch hook in src/hooks/useOptimizedData.ts, but I created a new useDebouncedValue hook for this PR instead.

Why?
The old hook only filters arrays by a single text field. We needed to filter 5 different fields at once (Pool name, Bull symbol, Bear symbol, Chain, and Oracle), so a generic value-based debounce was much better and let the input stay instantly responsive.

Question:
I also noticed the old useDebouncedSearch hook isn't being used anywhere in the codebase. Would you like me to delete that unused code in this PR to clean things up, or just leave it alone? Let me know!

@DengreSarthak

Copy link
Copy Markdown
Member

@Atharva0506 Yes, go ahead and remoVe it.

@Atharva0506

Copy link
Copy Markdown
Member Author

@DengreSarthak removed!

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🤖 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/useOptimizedData.ts`:
- Around line 85-92: The issue is that aborted requests still execute their
finally blocks and can set loading=false even while newer requests are pending,
causing the loading state to be incorrectly cleared. To fix this, add a request
ID or version tracking mechanism by creating a unique identifier when
instantiating the AbortController in the request initialization (around lines
85-92), store this identifier in a ref, and then in the finally block (lines
142-145) check that the stored request ID matches the current request ID before
setting loading=false. This ensures only the most recent request can clear the
loading state.
- Around line 149-165: When the key changes and no cached entry exists, the
previous data remains visible until the new fetch completes, causing stale data
from a different key to appear. In the useEffect hook, before calling
fetchData() without arguments (in the uncached fetch path after the cache
check), add a call to setData with an appropriate initial value (such as null or
an empty array depending on the data structure) to clear the previous key's data
immediately, ensuring users don't see stale cross-key data while the new fetch
is in progress.
- Around line 1-5: The useOptimizedData.ts hook module uses React client-side
hooks (useState, useEffect, useCallback, useMemo, useRef) but is missing the
required "use client" directive for Next.js. Add "use client" as the very first
line of the file, before all import statements, to explicitly mark this module
as a client component boundary.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0fcbeb25-46cb-456a-b5d1-e429f5b13b66

📥 Commits

Reviewing files that changed from the base of the PR and between 716123f and 35ff958.

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

Comment thread src/hooks/useOptimizedData.ts
Comment thread src/hooks/useOptimizedData.ts
Comment thread src/hooks/useOptimizedData.ts
@github-actions

Copy link
Copy Markdown

⚠️ This PR has merge conflicts.

Please resolve the merge conflicts before review.

Your PR will only be reviewed by a maintainer after all conflicts have been resolved.

📺 Watch this video to understand why conflicts occur and how to resolve them:
https://www.youtube.com/watch?v=Sqsz1-o7nXk

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: Add Debounced Search to Explore Pools Page to Optimize Performance

2 participants