Skip to content

Conversation

@jaysomani
Copy link
Contributor

@jaysomani jaysomani commented Dec 3, 2025

What does this PR do?

Fixes a bug in the SearchQuery component where the search input field and "Clear Search" button were not working correctly.

The Problem:

  • When users typed in the search field, sometimes the input would become unresponsive or not update properly
  • When users clicked the "Clear Search" button, the search would briefly clear but then immediately re-apply the old search term, causing a "flash" effect where results would appear and then disappear again
  • The search input value would not sync properly when the URL changed externally (e.g., via Clear Search button or browser navigation)

Root Cause:
The component used a single $effect that watched both inputValue and page.url.searchParams, which created a feedback loop. When the URL changed (e.g., Clear Search removed ?search=), the effect would detect a mismatch and call runSearch(inputValue) again, re-adding the old search value to the URL.

The Solution:

  • Separated the sync logic into two distinct effects with proper tracking:
    1. URL → Input sync: When the URL search parameter changes externally (Clear Search, navigation), update the input value
    2. Input → URL sync: When the user types, update the URL (with debounce)
  • Added previous value tracking to distinguish between user-initiated changes and external URL changes, preventing the feedback loop

Before Fix:

  • ❌ Clear Search would flash results then disappear
  • ❌ Search input sometimes became unresponsive
  • ❌ Old search value would re-appear after clearing

After Fix:

  • ✅ Clear Search works correctly and stays cleared
  • ✅ Search input is fully responsive
  • ✅ No feedback loops or flashing results

Related PRs and Issues

#2656

Have you read the Contributing Guidelines on issues?

Yes, I have read the Contributing Guidelines.

Summary by CodeRabbit

  • Bug Fixes
    • Improved search input synchronization to prevent feedback loops between typed queries and URL parameters.
    • Split URL→input and input→URL handling so external URL changes and user typing are reliably distinguished.
    • Preserved debounced search behavior and ensured cleanup on component destruction; clear-input behavior remains consistent.

✏️ Tip: You can customize this high-level summary in your review settings.

@appwrite
Copy link

appwrite bot commented Dec 3, 2025

Console (appwrite/console)

Project ID: 688b7bf400350cbd60e9

Sites (1)
Site Status Logs Preview QR
 console-stage
688b7cf6003b1842c9dc
Failed Failed View Logs Preview URL QR Code

Tip

Trigger functions via HTTP, SDKs, events, webhooks, or scheduled cron jobs

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 3, 2025

Walkthrough

This change refactors URL↔input synchronization in the searchQuery Svelte component by introducing initialSearch, inputValue, previousInputValue, and previousUrlSearch. URL→input and input→URL logic are split into two guarded reactive effects that use untrack and previous-value comparisons to avoid feedback loops. The input effect updates when the URL search param changes; the URL effect updates (and triggers a debounced search) when the input changes. Debounce cleanup on component destroy and existing clearInput behavior are preserved.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: addressing search and clear search behavior issues in the SearchQuery component, which aligns with the changeset's purpose of fixing the feedback loop bug.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4d7cb51 and 442e16f.

📒 Files selected for processing (1)
  • src/lib/components/searchQuery.svelte
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/components/searchQuery.svelte

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
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: 0

🧹 Nitpick comments (1)
src/lib/components/searchQuery.svelte (1)

60-86: Well-structured two-effect approach to break the feedback loop.

The separation of concerns is clear:

  • Effect 1 (lines 61-70): Handles external URL changes by syncing to input
  • Effect 2 (lines 73-86): Handles user typing by syncing to URL via debounce

The key to preventing the feedback loop is updating both inputValue and previousInputValue together in Effect 1 (lines 66-67), so Effect 2's guard condition inputValue !== previousInputValue evaluates to false when the change originated from a URL update.

Optional consideration: Effect 1 reads inputValue on line 65, creating a dependency that causes this effect to run on every keystroke (though the urlSearch !== previousUrlSearch guard prevents any action). If performance becomes a concern, you could restructure to avoid this dependency:

     $effect(() => {
         const urlSearch = page.url.searchParams.get('search') ?? '';
         if (urlSearch !== previousUrlSearch) {
+            const currentInput = inputValue; // read once, but still creates dependency
             previousUrlSearch = urlSearch;
-            if (urlSearch !== inputValue) {
+            if (urlSearch !== currentInput) {
                 inputValue = urlSearch;
                 previousInputValue = urlSearch;
             }
         }
     });

However, given the guard condition, the current implementation is correct and the overhead is negligible.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 20874f5 and 93708e7.

📒 Files selected for processing (1)
  • src/lib/components/searchQuery.svelte (2 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx,svelte}: Import reusable modules from the src/lib directory using the $lib alias
Use minimal comments in code; reserve comments for TODOs or complex logic explanations
Use $lib, $routes, and $themes aliases instead of relative paths for module imports

Files:

  • src/lib/components/searchQuery.svelte
src/lib/components/**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for component file names and place them in src/lib/components/[feature]/ directory structure

Files:

  • src/lib/components/searchQuery.svelte
**/*.{ts,tsx,js,jsx,svelte,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4 spaces for indentation, single quotes, 100 character line width, and no trailing commas per Prettier configuration

Files:

  • src/lib/components/searchQuery.svelte
**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use Svelte 5 + SvelteKit 2 syntax with TypeScript for component development

Files:

  • src/lib/components/searchQuery.svelte
🔇 Additional comments (1)
src/lib/components/searchQuery.svelte (1)

26-29: Good state initialization pattern.

Capturing the initial URL state and using three tracking variables to distinguish user input changes from URL-driven changes is a clean approach to prevent the feedback loop described in the PR.

@jaysomani jaysomani requested a review from ItzNotABug December 27, 2025 11:22
Copy link
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: 0

🧹 Nitpick comments (1)
src/lib/components/searchQuery.svelte (1)

72-82: Consider extracting the duplicated assignment.

The previousInputValue = inputValue assignment appears in both branches and can be consolidated after the conditional.

🔎 Proposed refactor
     $effect(() => {
         if (inputValue !== previousInputValue) {
             const urlSearch = untrack(() => page.url.searchParams.get('search') ?? '');
             if (inputValue !== urlSearch) {
-                previousInputValue = inputValue;
                 runSearch(inputValue);
-            } else {
-                previousInputValue = inputValue;
             }
+            previousInputValue = inputValue;
         }
     });
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 93708e7 and 4d7cb51.

📒 Files selected for processing (1)
  • src/lib/components/searchQuery.svelte
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx,svelte}: Import reusable modules from the src/lib directory using the $lib alias
Use minimal comments in code; reserve comments for TODOs or complex logic explanations
Use $lib, $routes, and $themes aliases instead of relative paths for module imports

Files:

  • src/lib/components/searchQuery.svelte
src/lib/components/**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for component file names and place them in src/lib/components/[feature]/ directory structure

Files:

  • src/lib/components/searchQuery.svelte
**/*.{ts,tsx,js,jsx,svelte,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4 spaces for indentation, single quotes, 100 character line width, and no trailing commas per Prettier configuration

Files:

  • src/lib/components/searchQuery.svelte
**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use Svelte 5 + SvelteKit 2 syntax with TypeScript for component development

Files:

  • src/lib/components/searchQuery.svelte
🧠 Learnings (4)
📚 Learning: 2025-10-13T05:13:54.542Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2413
File: src/routes/(console)/project-[region]-[project]/databases/table.svelte:33-39
Timestamp: 2025-10-13T05:13:54.542Z
Learning: In Svelte 5, `import { page } from '$app/state'` provides a reactive state proxy that can be accessed directly (e.g., `page.params`), unlike the older `import { page } from '$app/stores'` which returns a readable store requiring the `$page` syntax for auto-subscription in components.

Applied to files:

  • src/lib/components/searchQuery.svelte
📚 Learning: 2025-10-05T09:41:40.439Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2398
File: src/routes/(console)/verify-email/+page.svelte:48-51
Timestamp: 2025-10-05T09:41:40.439Z
Learning: In SvelteKit 5, `page` imported from `$app/state` is a reactive state object (using runes), not a store. It should be accessed as `page.data` without the `$` prefix, unlike the store-based `$page` from `$app/stores` in earlier versions.

Applied to files:

  • src/lib/components/searchQuery.svelte
📚 Learning: 2025-11-25T03:15:27.539Z
Learnt from: CR
Repo: appwrite/console PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T03:15:27.539Z
Learning: Applies to **/*.svelte : Use Svelte 5 + SvelteKit 2 syntax with TypeScript for component development

Applied to files:

  • src/lib/components/searchQuery.svelte
📚 Learning: 2025-11-25T03:15:27.539Z
Learnt from: CR
Repo: appwrite/console PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T03:15:27.539Z
Learning: Applies to src/routes/**/*.svelte : Use SvelteKit file conventions: +page.svelte for components, +page.ts for data loaders, +layout.svelte for wrappers, +error.svelte for error handling, and dynamic route params in square brackets like [param]

Applied to files:

  • src/lib/components/searchQuery.svelte
🔇 Additional comments (2)
src/lib/components/searchQuery.svelte (2)

26-29: LGTM!

The tracking variables are correctly structured: inputValue as $state for reactive binding, while previousInputValue and previousUrlSearch are plain variables to avoid triggering unnecessary effect re-runs. Based on past review feedback, this is the intended pattern.


60-70: LGTM!

The URL → Input sync effect correctly uses untrack to read inputValue without creating a reactive dependency, preventing the feedback loop that was causing the "flash" issue. The guard logic properly distinguishes external URL changes from user input.

@ItzNotABug ItzNotABug merged commit baf47f8 into appwrite:main Dec 27, 2025
3 of 5 checks passed
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.

2 participants