From 08fe78813070c85d29761ddf41e388494841f359 Mon Sep 17 00:00:00 2001 From: Rune Botten Date: Mon, 12 Aug 2024 19:35:22 -0700 Subject: [PATCH 1/2] fix(core): avoid unnecessary re-renders in useDocumentPresence --- .../store/_legacy/presence/useDocumentPresence.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/sanity/src/core/store/_legacy/presence/useDocumentPresence.tsx b/packages/sanity/src/core/store/_legacy/presence/useDocumentPresence.tsx index 26a74f56b75..485ffc75de3 100644 --- a/packages/sanity/src/core/store/_legacy/presence/useDocumentPresence.tsx +++ b/packages/sanity/src/core/store/_legacy/presence/useDocumentPresence.tsx @@ -1,15 +1,20 @@ -import {useEffect, useState} from 'react' +import {isEqual} from 'lodash' +import {useEffect, useReducer} from 'react' import {usePresenceStore} from '../datastores' import {type DocumentPresence} from './types' +function presenceReducer(state: DocumentPresence[], next: DocumentPresence[]): DocumentPresence[] { + return isEqual(state, next) ? state : next +} + /** @internal */ export function useDocumentPresence(documentId: string): DocumentPresence[] { const presenceStore = usePresenceStore() - const [presence, setPresence] = useState([]) + const [presence, dispatch] = useReducer(presenceReducer, []) useEffect(() => { - const subscription = presenceStore.documentPresence(documentId).subscribe(setPresence) + const subscription = presenceStore.documentPresence(documentId).subscribe(dispatch) return () => { subscription.unsubscribe() } From 195016faaca6484eeaa5dbd6b88e7501fa4727ab Mon Sep 17 00:00:00 2001 From: Rune Botten Date: Mon, 12 Aug 2024 20:01:15 -0700 Subject: [PATCH 2/2] fix(core): make sure useDocumentPresence only emits when there are changes --- .../store/_legacy/presence/presence-store.ts | 51 ++++++++++--------- .../_legacy/presence/useDocumentPresence.tsx | 11 ++-- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/packages/sanity/src/core/store/_legacy/presence/presence-store.ts b/packages/sanity/src/core/store/_legacy/presence/presence-store.ts index 8e338b09c42..ab1e2de08f2 100644 --- a/packages/sanity/src/core/store/_legacy/presence/presence-store.ts +++ b/packages/sanity/src/core/store/_legacy/presence/presence-store.ts @@ -2,10 +2,11 @@ import {type BifurClient} from '@sanity/bifur-client' import {type User} from '@sanity/types' -import {flatten, groupBy, omit, uniq} from 'lodash' +import {flatten, groupBy, isEqual, omit, uniq} from 'lodash' import {nanoid} from 'nanoid' import { BehaviorSubject, + combineLatest, defer, EMPTY, from, @@ -19,7 +20,6 @@ import { auditTime, distinctUntilChanged, filter, - flatMap, map, mergeMapTo, scan, @@ -29,7 +29,6 @@ import { switchMapTo, takeUntil, tap, - toArray, withLatestFrom, } from 'rxjs/operators' @@ -292,29 +291,35 @@ export function __tmp_wrap_presenceStore(context: { ), ) + // Create a single shared observable for all documents + const allDocumentsPresence$ = combineLatest([allSessions$, debugIntrospect$]).pipe( + map(([userAndSessions, debugIntrospect]) => + userAndSessions + .flatMap((userAndSession) => + (userAndSession.session.locations || []).map((location) => ({ + documentId: location.documentId, + presence: { + user: userAndSession.user, + lastActiveAt: userAndSession.session.lastActiveAt, + path: location.path || [], + sessionId: userAndSession.session.sessionId, + selection: location?.selection, + }, + })), + ) + .filter((item) => debugIntrospect || item.presence.sessionId !== SESSION_ID), + ), + shareReplay(1), + ) + // export const documentPresence = (documentId: string): Observable => { - return allSessions$.pipe( - withLatestFrom(debugIntrospect$), - switchMap(([userAndSessions, debugIntrospect]) => - from(userAndSessions).pipe( - filter( - (userAndSession) => debugIntrospect || userAndSession.session.sessionId !== SESSION_ID, - ), - flatMap((userAndSession) => - (userAndSession.session.locations || []) - .filter((item) => item.documentId === documentId) - .map((location) => ({ - user: userAndSession.user, - lastActiveAt: userAndSession.session.lastActiveAt, - path: location.path || [], - sessionId: userAndSession.session.sessionId, - selection: location?.selection, - })), - ), - toArray(), - ), + return allDocumentsPresence$.pipe( + map((allPresence) => + allPresence.filter((item) => item.documentId === documentId).map((item) => item.presence), ), + // Only emit if the presence has changed for this document id + distinctUntilChanged((prev, curr) => isEqual(prev, curr)), ) } diff --git a/packages/sanity/src/core/store/_legacy/presence/useDocumentPresence.tsx b/packages/sanity/src/core/store/_legacy/presence/useDocumentPresence.tsx index 485ffc75de3..26a74f56b75 100644 --- a/packages/sanity/src/core/store/_legacy/presence/useDocumentPresence.tsx +++ b/packages/sanity/src/core/store/_legacy/presence/useDocumentPresence.tsx @@ -1,20 +1,15 @@ -import {isEqual} from 'lodash' -import {useEffect, useReducer} from 'react' +import {useEffect, useState} from 'react' import {usePresenceStore} from '../datastores' import {type DocumentPresence} from './types' -function presenceReducer(state: DocumentPresence[], next: DocumentPresence[]): DocumentPresence[] { - return isEqual(state, next) ? state : next -} - /** @internal */ export function useDocumentPresence(documentId: string): DocumentPresence[] { const presenceStore = usePresenceStore() - const [presence, dispatch] = useReducer(presenceReducer, []) + const [presence, setPresence] = useState([]) useEffect(() => { - const subscription = presenceStore.documentPresence(documentId).subscribe(dispatch) + const subscription = presenceStore.documentPresence(documentId).subscribe(setPresence) return () => { subscription.unsubscribe() }