diff --git a/packages/sanity/src/core/store/_legacy/ResourceCacheProvider.tsx b/packages/sanity/src/core/store/_legacy/ResourceCacheProvider.tsx index c6b28cb9c7d..234feb7096f 100644 --- a/packages/sanity/src/core/store/_legacy/ResourceCacheProvider.tsx +++ b/packages/sanity/src/core/store/_legacy/ResourceCacheProvider.tsx @@ -1,4 +1,4 @@ -import {type ReactNode, useContext, useMemo} from 'react' +import {type ReactNode, useContext, useState} from 'react' import {ResourceCacheContext} from 'sanity/_singletons' import {createMultiKeyWeakMap, type MultiKeyWeakMap} from './createMultiKeyWeakMap' @@ -16,7 +16,7 @@ export interface ResourceCacheProviderProps { /** @internal */ export function ResourceCacheProvider({children}: ResourceCacheProviderProps) { - const resourceCache = useMemo((): ResourceCache => { + const [resourceCache] = useState((): ResourceCache => { const namespaces = new Map() // this is used to replace the `null` values in any `dependencies` so that @@ -41,7 +41,7 @@ export function ResourceCacheProvider({children}: ResourceCacheProviderProps) { namespaceMap.set(dependenciesWithoutNull, value) }, } - }, []) + }) return ( {children} diff --git a/packages/sanity/src/core/store/_legacy/datastores.ts b/packages/sanity/src/core/store/_legacy/datastores.ts index f69be4ae70d..ad930c2412b 100644 --- a/packages/sanity/src/core/store/_legacy/datastores.ts +++ b/packages/sanity/src/core/store/_legacy/datastores.ts @@ -20,7 +20,7 @@ import {fetchFeatureToggle} from './document/document-pair/utils/fetchFeatureTog import {type OutOfSyncError} from './document/utils/sequentializeListenerEvents' import {createGrantsStore, type GrantsStore} from './grants' import {createHistoryStore, type HistoryStore} from './history' -import {__tmp_wrap_presenceStore, type PresenceStore} from './presence/presence-store' +import {createPresenceStore, type PresenceStore} from './presence/presence-store' import {createProjectStore, type ProjectStore} from './project' import {useResourceCache} from './ResourceCacheProvider' import {createUserStore, type UserStore} from './user' @@ -231,7 +231,7 @@ export function usePresenceStore(): PresenceStore { resourceCache.get({ namespace: 'presenceStore', dependencies: [bifur, connectionStatusStore, userStore], - }) || __tmp_wrap_presenceStore({bifur, connectionStatusStore, userStore}) + }) || createPresenceStore({bifur, connectionStatusStore, userStore}) resourceCache.set({ namespace: 'presenceStore', 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 ab1e2de08f2..24eb2abca43 100644 --- a/packages/sanity/src/core/store/_legacy/presence/presence-store.ts +++ b/packages/sanity/src/core/store/_legacy/presence/presence-store.ts @@ -114,7 +114,7 @@ function setSessionId(id: string) { export const SESSION_ID = getSessionId() || setSessionId(generate()) /** @internal */ -export function __tmp_wrap_presenceStore(context: { +export function createPresenceStore(context: { bifur: BifurClient connectionStatusStore: ConnectionStatusStore userStore: UserStore diff --git a/packages/sanity/src/core/store/_legacy/presence/useDocumentPresence.tsx b/packages/sanity/src/core/store/_legacy/presence/useDocumentPresence.tsx index 26a74f56b75..d7b04588d06 100644 --- a/packages/sanity/src/core/store/_legacy/presence/useDocumentPresence.tsx +++ b/packages/sanity/src/core/store/_legacy/presence/useDocumentPresence.tsx @@ -1,19 +1,19 @@ -import {useEffect, useState} from 'react' +import {startTransition, useEffect, useReducer} from 'react' +import {useObservable} from 'react-rx' +import {of} from 'rxjs' import {usePresenceStore} from '../datastores' import {type DocumentPresence} from './types' +const initial: DocumentPresence[] = [] +const fallback = of(initial) + /** @internal */ export function useDocumentPresence(documentId: string): DocumentPresence[] { - const presenceStore = usePresenceStore() - const [presence, setPresence] = useState([]) + const [mounted, mount] = useReducer(() => true, false) + // Using `startTransition` here ensures that rapid re-renders that affect the deps used by `usePresenceStore` delay the transition to `mounted=true`, thus avoiding creating websocket connections that will be closed immediately. + useEffect(() => startTransition(mount), []) - useEffect(() => { - const subscription = presenceStore.documentPresence(documentId).subscribe(setPresence) - return () => { - subscription.unsubscribe() - } - }, [documentId, presenceStore]) - - return presence + const presenceStore = usePresenceStore() + return useObservable(mounted ? presenceStore.documentPresence(documentId) : fallback, initial) } diff --git a/packages/sanity/src/core/store/_legacy/presence/useGlobalPresence.tsx b/packages/sanity/src/core/store/_legacy/presence/useGlobalPresence.tsx index 928976cfaab..694f7a565d0 100644 --- a/packages/sanity/src/core/store/_legacy/presence/useGlobalPresence.tsx +++ b/packages/sanity/src/core/store/_legacy/presence/useGlobalPresence.tsx @@ -1,20 +1,19 @@ -import {useEffect, useState} from 'react' +import {startTransition, useEffect, useReducer} from 'react' +import {useObservable} from 'react-rx' +import {of} from 'rxjs' import {usePresenceStore} from '../datastores' import {type GlobalPresence} from './types' +const initial: GlobalPresence[] = [] +const fallback = of(initial) + /** @internal */ export function useGlobalPresence(): GlobalPresence[] { - const [presence, setPresence] = useState([]) - const presenceStore = usePresenceStore() - - useEffect(() => { - const subscription = presenceStore.globalPresence$.subscribe(setPresence) + const [mounted, mount] = useReducer(() => true, false) + // Using `startTransition` here ensures that rapid re-renders that affect the deps used by `usePresenceStore` delay the transition to `mounted=true`, thus avoiding creating websocket connections that will be closed immediately. + useEffect(() => startTransition(mount), []) - return () => { - subscription.unsubscribe() - } - }, [presenceStore]) - - return presence + const presenceStore = usePresenceStore() + return useObservable(mounted ? presenceStore.globalPresence$ : fallback, initial) }