Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 65 additions & 6 deletions src/components/editor-input/mention-plugin/mention-hooks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { useEffect, useState, useRef } from 'react';
import { OptionsArray } from './mention-plugin';

// Wrapper class for string keys
class StringKey {
value: string;

constructor( value: string ) {
this.value = value;
}
}

type TBy<T> =
T extends Array<infer U>
? U extends Record<string, unknown>
Expand All @@ -14,8 +23,39 @@ function useMentionLookupService<T = OptionsArray>(
by: TBy<T> = 'name' as TBy<T>
): OptionsArray {
const [ results, setResults ] = useState<OptionsArray>( [] );
// Create an instance-specific cache using useRef
const mentionsCacheRef = useRef<Map<string, OptionsArray | null>>( new Map() );

// Create instance-specific cache using WeakMap
const mentionsCacheRef = useRef<WeakMap<StringKey, OptionsArray | null>>( new WeakMap() );

// Store reference to key objects
const keysRef = useRef<Map<string, StringKey>>( new Map() );

// Track the previous options reference
const prevOptionsRef = useRef<T>( options );

// Clear cache when options change
useEffect( () => {
// Check if options actually changed (not just re-rendered with same data)
if ( prevOptionsRef.current !== options ) {
// Options changed, clear cache
mentionsCacheRef.current = new WeakMap();
keysRef.current.clear();
prevOptionsRef.current = options;

// Re-run search if we have an active search
if ( mentionString !== null ) {
// Trigger search with new options
lookupService.search(
options,
mentionString,
( newResults ) => {
setResults( newResults as OptionsArray );
},
by
);
}
}
}, [ options, by, mentionString ] );

useEffect( () => {
if ( mentionString === null ) {
Expand All @@ -24,24 +64,43 @@ function useMentionLookupService<T = OptionsArray>(
}

const mentionsCache = mentionsCacheRef.current;
const cachedResults = mentionsCache.get( mentionString );
const keysMap = keysRef.current;

// Get or create key object for this string
let keyObj = keysMap.get( mentionString );
if ( ! keyObj ) {
keyObj = new StringKey( mentionString );
keysMap.set( mentionString, keyObj );
}

const cachedResults = mentionsCache.get( keyObj );
if ( cachedResults === null ) {
return;
} else if ( cachedResults !== undefined ) {
setResults( cachedResults );
return;
}

mentionsCache.set( mentionString, null );
mentionsCache.set( keyObj, null );
lookupService.search(
options,
mentionString,
( newResults ) => {
mentionsCache.set( mentionString, newResults as OptionsArray );
setResults( newResults as OptionsArray );
// Ensure the key object still exists
const currentKeyObj = keysMap.get( mentionString );
if ( currentKeyObj ) {
mentionsCache.set( currentKeyObj, newResults as OptionsArray );
setResults( newResults as OptionsArray );
}
},
by
);

// Periodically clean up old keys
if ( keysMap.size > 100 ) {
const keysToKeep = Array.from( keysMap.entries() ).slice( -50 );
keysRef.current = new Map( keysToKeep );
}
}, [ mentionString, options, by ] );

return results;
Expand Down
Loading