Skip to content

Commit 9e9fc5b

Browse files
Make FixedSizeQueueCache slightly faster
1 parent fe3c100 commit 9e9fc5b

File tree

1 file changed

+31
-41
lines changed

1 file changed

+31
-41
lines changed

Diff for: src/components/MessageInput/hooks/messageComposer/useMessageComposer.ts

+31-41
Original file line numberDiff line numberDiff line change
@@ -5,66 +5,55 @@ import { useChannelStateContext, useMessageInputContext } from '../../../../cont
55
import type { LocalMessage } from 'stream-chat';
66
import { useLegacyThreadContext } from '../../../Thread';
77

8-
class FixedSizeQueueCache<T> {
9-
private elements: Array<T>;
8+
class FixedSizeQueueCache<K, T> {
9+
private keys: Array<K>;
1010
private size: number;
11+
private valueByKey: Map<K, T>;
1112
constructor(size: number) {
1213
if (!size) throw new Error('Size must be greater than 0');
13-
this.elements = [];
14+
this.keys = [];
1415
this.size = size;
16+
this.valueByKey = new Map();
1517
}
1618

17-
add(...values: T[]) {
18-
const pushableValues =
19-
values.length > this.size ? values.slice(values.length - this.size) : values;
19+
add(key: K, value: T) {
20+
const index = this.keys.indexOf(key);
2021

21-
if (pushableValues.length === this.size) {
22-
// this.elements.splice(0, this.size - 1);
23-
this.elements.length = 0;
24-
this.elements.push(...pushableValues);
25-
} else {
26-
for (const value of pushableValues) {
27-
if (this.elements.length >= this.size) {
28-
this.elements.shift();
29-
}
22+
if (index > -1) {
23+
this.keys.splice(this.keys.indexOf(key), 1);
24+
} else if (this.keys.length >= this.size) {
25+
const elementKey = this.keys.shift();
3026

31-
this.elements.push(value);
27+
if (elementKey) {
28+
this.valueByKey.delete(elementKey);
3229
}
3330
}
34-
}
3531

36-
// returns value without shifting it to the end of the array
37-
peek(predicate: (element: T) => boolean) {
38-
// start searching from the most recent (end of array)
39-
for (let index = 0; index < this.elements.length; index++) {
40-
const element = this.elements[this.elements.length - 1 - index];
41-
const predicateResult = predicate(element);
32+
this.keys.push(key);
33+
this.valueByKey.set(key, value);
34+
}
4235

43-
if (predicateResult) return element;
44-
}
36+
peek(key: K) {
37+
const value = this.valueByKey.get(key);
4538

46-
return null;
39+
return value;
4740
}
4841

49-
get(predicate: (element: T) => boolean) {
50-
const foundElement = this.peek(predicate);
42+
get(key: K) {
43+
const foundElement = this.peek(key);
5144

52-
if (foundElement && this.elements.indexOf(foundElement) !== this.size - 1) {
53-
this.elements.splice(this.elements.indexOf(foundElement), 1);
54-
this.elements.push(foundElement);
45+
if (foundElement && this.keys.indexOf(key) !== this.size - 1) {
46+
this.keys.splice(this.keys.indexOf(key), 1);
47+
this.keys.push(key);
5548
}
5649

5750
return foundElement;
5851
}
59-
60-
toArray() {
61-
return [...this.elements];
62-
}
6352
}
6453

6554
export type UseMessageComposerParams = unknown;
6655

67-
const queueCache = new FixedSizeQueueCache<MessageComposer>(64);
56+
const queueCache = new FixedSizeQueueCache<string, MessageComposer>(64);
6857
// eslint-disable-next-line @typescript-eslint/no-unused-vars
6958
export const useMessageComposer = (_unused: UseMessageComposerParams = {}) => {
7059
const { channel } = useChannelStateContext();
@@ -99,7 +88,7 @@ export const useMessageComposer = (_unused: UseMessageComposerParams = {}) => {
9988
if (cachedEditedMessage) {
10089
const tag = `edited-message-${cachedEditedMessage.id}`;
10190

102-
const element = queueCache.get((element) => element.tag === tag);
91+
const element = queueCache.get(tag);
10392
if (element) return element;
10493

10594
const c = new MessageComposer({
@@ -108,15 +97,13 @@ export const useMessageComposer = (_unused: UseMessageComposerParams = {}) => {
10897
tag,
10998
});
11099

111-
// FIXME: don't like this side effect here
112-
queueCache.add(c);
113100
return c;
114101
} else if (threadInstance) {
115102
return threadInstance.messageComposer;
116103
} else if (cachedParentMessage) {
117104
const tag = `parent-message-${cachedParentMessage.id}`;
118105

119-
const element = queueCache.get((element) => element.tag === tag);
106+
const element = queueCache.get(tag);
120107
if (element) return element;
121108

122109
const c = new MessageComposer({
@@ -126,7 +113,6 @@ export const useMessageComposer = (_unused: UseMessageComposerParams = {}) => {
126113
threadId: cachedParentMessage.id,
127114
});
128115

129-
queueCache.add(c);
130116
return c;
131117
} else if (channel) {
132118
return channel.messageComposer;
@@ -142,6 +128,10 @@ export const useMessageComposer = (_unused: UseMessageComposerParams = {}) => {
142128
threadInstance,
143129
]);
144130

131+
if (!queueCache.peek(messageComposer.tag)) {
132+
queueCache.add(messageComposer.tag, messageComposer);
133+
}
134+
145135
useEffect(() => {
146136
messageComposer.registerSubscriptions();
147137
return () => {

0 commit comments

Comments
 (0)