Skip to content

Commit 584e388

Browse files
committed
chore: add comment explaining arch decision
1 parent b40ec93 commit 584e388

File tree

1 file changed

+18
-0
lines changed

1 file changed

+18
-0
lines changed

package/src/state-store/message-overlay-store.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,24 @@ export const useOverlayController = () => {
148148
return useStateStore(overlayStore, selector);
149149
};
150150

151+
/**
152+
* NOTE:
153+
* Do not swap this back to `useStateStore(closingPortalLayoutsStore, selector)`.
154+
*
155+
* Why this is special:
156+
* - `layouts` is a dynamic-key map (hosts are added/removed at runtime)
157+
* - We only need React updates when the key set changes (add/remove/reset)
158+
* - Per-layout movement is already on UI thread via `entry.layout.value`
159+
*
160+
* Why `useStateStore` is unsafe here:
161+
* - Both `stream-chat`'s `subscribeWithSelector` and our `useStateStore` snapshot
162+
* comparator use an asymmetric key comparison (they iterate previous keys only)
163+
* - That means `{}` -> `{ newHost: entry }` can be treated as "no change"
164+
* - When that happens, overlay slots never mount even though registration has run
165+
*
166+
* `useSyncExternalStore` with raw store subscription avoids that selector compare path,
167+
* so add/remove of hosts is always treated as observable.
168+
*/
151169
export const useClosingPortalLayouts = () => {
152170
const subscribe = useCallback(
153171
(listener: () => void) =>

0 commit comments

Comments
 (0)