perf: memoize room-list sorts + gate DM-preview listener (PERF-2/4/5)

PERF-2 (RoomMentionAutocomplete): the #-mention list did
`useAtomValue(allRoomsAtom).sort(...)` inline — `.sort()` MUTATED the shared
allRoomsAtom array in place (reordering it for ~27 other consumers) and re-ran
the O(N log N) getRoom compare every keystroke. Copy then memoize:
`useMemo(() => [...allRoomsList].sort(factoryRoomIdByActivity(mx)), ...)`.

PERF-4 (SearchFilters): the room-filter A-Z sort ran every render; wrap in
useMemo keyed on [searchResult, roomList, mx].

PERF-5 (useRoomLatestRenderedEvent + RoomNavItem): the hook registered a GLOBAL
client `Decrypted` listener for every nav item, but its result is only used for
DM rows. Add an `enabled` param (default true) that skips all work + listeners
when false; RoomNavItem passes `!!direct`. The only other caller keeps the
default.

Verified behavior-preserving by two review passes (PERF-2 also fixes a real
shared-atom mutation bug).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 21:05:39 -04:00
co-authored by Claude Opus 4.8
parent 8a15405189
commit 4708a17961
4 changed files with 27 additions and 6 deletions
@@ -1,4 +1,4 @@
import React, { KeyboardEvent as ReactKeyboardEvent, useCallback, useEffect } from 'react';
import React, { KeyboardEvent as ReactKeyboardEvent, useCallback, useEffect, useMemo } from 'react';
import { Editor } from 'slate';
import { Avatar, Icon, Icons, MenuItem, Text } from 'folds';
import { JoinRule, MatrixClient } from 'matrix-js-sdk';
@@ -80,7 +80,14 @@ export function RoomMentionAutocomplete({
const mx = useMatrixClient();
const mDirects = useAtomValue(mDirectAtom);
const allRooms = useAtomValue(allRoomsAtom).sort(factoryRoomIdByActivity(mx));
// Copy before sorting: `.sort()` mutates in place, and `allRoomsAtom`'s array
// is shared app-wide — sorting it here reorders it for every other consumer.
// Also memoize so a keystroke doesn't re-run the O(N log N) getRoom compare.
const allRoomsList = useAtomValue(allRoomsAtom);
const allRooms = useMemo(
() => [...allRoomsList].sort(factoryRoomIdByActivity(mx)),
[allRoomsList, mx],
);
const [result, search, resetSearch] = useAsyncSearch(
allRooms,