From e50ab161cae7d04a828f3938038b9297667997fc Mon Sep 17 00:00:00 2001 From: VipinDevelops Date: Thu, 18 Dec 2025 17:27:04 +0530 Subject: [PATCH 1/2] fix: debounce for mention search --- packages/editor/package.json | 2 + .../mentions/mentions-list-dropdown.tsx | 53 +++++++++++++------ pnpm-lock.yaml | 6 +++ 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/packages/editor/package.json b/packages/editor/package.json index 0f3b13f50fc..e981bfa3f62 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -62,6 +62,7 @@ "@tiptap/react": "^2.22.3", "@tiptap/starter-kit": "^2.22.3", "@tiptap/suggestion": "^2.22.3", + "lodash-es": "catalog:", "buffer": "^6.0.3", "emoji-regex": "^10.3.0", "highlight.js": "^11.8.0", @@ -85,6 +86,7 @@ "@types/node": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", + "@types/lodash-es": "catalog:", "postcss": "^8.4.38", "tsdown": "catalog:", "typescript": "catalog:" diff --git a/packages/editor/src/core/extensions/mentions/mentions-list-dropdown.tsx b/packages/editor/src/core/extensions/mentions/mentions-list-dropdown.tsx index d532290f21a..3b8f81b699b 100644 --- a/packages/editor/src/core/extensions/mentions/mentions-list-dropdown.tsx +++ b/packages/editor/src/core/extensions/mentions/mentions-list-dropdown.tsx @@ -1,7 +1,17 @@ import { FloatingOverlay } from "@floating-ui/react"; import type { SuggestionProps } from "@tiptap/suggestion"; -import { forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react"; +import { + forwardRef, + useCallback, + useEffect, + useImperativeHandle, + useLayoutEffect, + useMemo, + useRef, + useState, +} from "react"; import { v4 as uuidv4 } from "uuid"; +import { debounce } from "lodash-es"; // plane utils import { useOutsideClickDetector } from "@plane/hooks"; import { cn } from "@plane/utils"; @@ -75,23 +85,34 @@ export const MentionsListDropdown = forwardRef(function MentionsListDropdown(pro }); }, [sections]); - // fetch mention sections based on query - useEffect(() => { - const fetchSuggestions = async () => { - setIsLoading(true); - try { - const sectionsResponse = await searchCallback?.(query); - if (sectionsResponse) { - setSections(sectionsResponse); + // create debounced search callback + const debouncedSearchCallback = useMemo( + () => + debounce(async (searchQuery: string) => { + setIsLoading(true); + try { + const sectionsResponse = await searchCallback?.(searchQuery); + if (sectionsResponse) { + setSections(sectionsResponse); + } + } catch (error) { + console.error("Failed to fetch suggestions:", error); + } finally { + setIsLoading(false); } - } catch (error) { - console.error("Failed to fetch suggestions:", error); - } finally { - setIsLoading(false); - } + }, 300), + [searchCallback] + ); + + useEffect(() => { + if (query) { + void debouncedSearchCallback(query); + } + + return () => { + debouncedSearchCallback.cancel(); }; - fetchSuggestions(); - }, [query, searchCallback]); + }, [query, debouncedSearchCallback]); // scroll to the dropdown item when navigating via keyboard useLayoutEffect(() => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 231d572a054..95b477f47eb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -950,6 +950,9 @@ importers: linkifyjs: specifier: ^4.3.2 version: 4.3.2 + lodash-es: + specifier: 'catalog:' + version: 4.17.21 lowlight: specifier: ^3.0.0 version: 3.3.0 @@ -993,6 +996,9 @@ importers: '@plane/typescript-config': specifier: workspace:* version: link:../typescript-config + '@types/lodash-es': + specifier: 'catalog:' + version: 4.17.12 '@types/node': specifier: 'catalog:' version: 22.12.0 From 8eef915a45f861bb9493093b7642ab35147cc185 Mon Sep 17 00:00:00 2001 From: VipinDevelops Date: Thu, 18 Dec 2025 17:40:31 +0530 Subject: [PATCH 2/2] fix: update method --- .../mentions/mentions-list-dropdown.tsx | 51 +++++++++---------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/packages/editor/src/core/extensions/mentions/mentions-list-dropdown.tsx b/packages/editor/src/core/extensions/mentions/mentions-list-dropdown.tsx index 3b8f81b699b..e2b3ec7292c 100644 --- a/packages/editor/src/core/extensions/mentions/mentions-list-dropdown.tsx +++ b/packages/editor/src/core/extensions/mentions/mentions-list-dropdown.tsx @@ -1,15 +1,6 @@ import { FloatingOverlay } from "@floating-ui/react"; import type { SuggestionProps } from "@tiptap/suggestion"; -import { - forwardRef, - useCallback, - useEffect, - useImperativeHandle, - useLayoutEffect, - useMemo, - useRef, - useState, -} from "react"; +import { forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react"; import { v4 as uuidv4 } from "uuid"; import { debounce } from "lodash-es"; // plane utils @@ -85,34 +76,38 @@ export const MentionsListDropdown = forwardRef(function MentionsListDropdown(pro }); }, [sections]); - // create debounced search callback - const debouncedSearchCallback = useMemo( - () => - debounce(async (searchQuery: string) => { - setIsLoading(true); - try { - const sectionsResponse = await searchCallback?.(searchQuery); - if (sectionsResponse) { - setSections(sectionsResponse); - } - } catch (error) { - console.error("Failed to fetch suggestions:", error); - } finally { - setIsLoading(false); + // debounced search callback + const debouncedSearchCallback = useCallback( + debounce(async (searchQuery: string) => { + setIsLoading(true); + try { + const sectionsResponse = await searchCallback?.(searchQuery); + if (sectionsResponse) { + setSections(sectionsResponse); } - }, 300), + } catch (error) { + console.error("Failed to fetch suggestions:", error); + } finally { + setIsLoading(false); + } + }, 300), [searchCallback] ); + // trigger debounced search when query changes useEffect(() => { if (query) { void debouncedSearchCallback(query); } + }, [query, debouncedSearchCallback]); - return () => { + // cancel pending debounced calls on unmount + useEffect( + () => () => { debouncedSearchCallback.cancel(); - }; - }, [query, debouncedSearchCallback]); + }, + [debouncedSearchCallback] + ); // scroll to the dropdown item when navigating via keyboard useLayoutEffect(() => {