@@ -134,6 +134,9 @@ export const subscribeToDocumentChanges = (handle: DocHandle<DocumentContent>) =
134134 }
135135
136136 const entityTypes = new Set < string > ( ) ;
137+ // collect all query entries that changed and only at the end make one copy to change the
138+ // reference to reduce the amount of O(n) operations per query to 1
139+ const touchedQueries = new Set < Array < string > > ( ) ;
137140
138141 // loop over all changed entities and update the cache
139142 for ( const entityId of changedEntities ) {
@@ -155,6 +158,7 @@ export const subscribeToDocumentChanges = (handle: DocHandle<DocumentContent>) =
155158 } else {
156159 query . data . push ( decoded ) ;
157160 }
161+ touchedQueries . add ( [ typeName , 'all' ] ) ;
158162 }
159163
160164 entityTypes . add ( typeName ) ;
@@ -173,12 +177,23 @@ export const subscribeToDocumentChanges = (handle: DocHandle<DocumentContent>) =
173177 const index = query . data . findIndex ( ( entity ) => entity . id === entityId ) ;
174178 if ( index !== - 1 ) {
175179 query . data . splice ( index , 1 ) ;
180+ touchedQueries . add ( [ affectedTypeName , 'all' ] ) ;
176181 }
177182 }
178183 }
179184 }
180185 }
181186
187+ for ( const [ typeName , queryKey ] of touchedQueries ) {
188+ const cacheEntry = decodedEntitiesCache . get ( typeName ) ;
189+ if ( ! cacheEntry ) continue ;
190+
191+ const query = cacheEntry . queries . get ( queryKey ) ;
192+ if ( ! query ) continue ;
193+
194+ query . data = [ ...query . data ] ; // must be a new reference for React.useSyncExternalStore
195+ }
196+
182197 // invoke all the listeners per type
183198 for ( const typeName of entityTypes ) {
184199 const cacheEntry = decodedEntitiesCache . get ( typeName ) ;
@@ -195,7 +210,6 @@ export const subscribeToDocumentChanges = (handle: DocHandle<DocumentContent>) =
195210 handle . on ( 'change' , onChange ) ;
196211
197212 return ( ) => {
198- console . log ( 'unsubscribe document changes' ) ;
199213 handle . off ( 'change' , onChange ) ;
200214 decodedEntitiesCache . clear ( ) ; // currently we only support exactly one space
201215 } ;
@@ -317,34 +331,18 @@ export function findMany<const S extends AnyNoContext>(
317331export function subscribeToFindMany < const S extends AnyNoContext > (
318332 handle : DocHandle < DocumentContent > ,
319333 type : S ,
320- ) : { listener : ( ) => ( ) => void ; getEntities : ( ) => Readonly < Array < Entity < S > > > ; unsubscribe : ( ) => void } {
334+ ) : {
335+ subscribe : ( callback : ( ) => void ) => ( ) => void ;
336+ getEntities : ( ) => Readonly < Array < Entity < S > > > ;
337+ } {
321338 const queryKey = 'all' ;
322339 const decode = Schema . decodeUnknownSync ( type ) ;
323340 // TODO: what's the right way to get the name of the type?
324341 // @ts -expect-error name is defined
325342 const typeName = type . name ;
326343
327344 const getEntities = ( ) => {
328- const entities = decodedEntitiesCache . get ( typeName ) ?. queries . get ( queryKey ) ?. data ?? [ ] ;
329- return entities ;
330- } ;
331-
332- const listener = ( ) => {
333- return ( ) => undefined ;
334- } ;
335-
336- const unsubscribe = ( ) => {
337- const query = decodedEntitiesCache . get ( typeName ) ?. queries . get ( queryKey ) ;
338- if ( query ?. listeners ) {
339- query . listeners = query . listeners . filter ( ( cachedListener ) => cachedListener !== listener ) ;
340- console . log ( 'unsubscribe query' , query . listeners ) ;
341- }
342-
343- documentChangeListener . subscribedQueriesCount -- ;
344- if ( documentChangeListener . subscribedQueriesCount === 0 ) {
345- documentChangeListener . unsubscribe ?.( ) ;
346- documentChangeListener . unsubscribe = undefined ;
347- }
345+ return decodedEntitiesCache . get ( typeName ) ?. queries . get ( queryKey ) ?. data ?? [ ] ;
348346 } ;
349347
350348 const entities = findMany ( handle , type ) ;
@@ -366,10 +364,6 @@ export function subscribeToFindMany<const S extends AnyNoContext>(
366364 query . data . push ( entity ) ;
367365 }
368366 }
369-
370- if ( query ?. listeners ) {
371- query . listeners . push ( listener ) ;
372- }
373367 } else {
374368 const entitiesMap = new Map ( ) ;
375369 for ( const entity of entities ) {
@@ -380,7 +374,7 @@ export function subscribeToFindMany<const S extends AnyNoContext>(
380374
381375 queries . set ( queryKey , {
382376 data : entities ,
383- listeners : [ listener ] ,
377+ listeners : [ ] ,
384378 } ) ;
385379
386380 decodedEntitiesCache . set ( typeName , {
@@ -390,12 +384,31 @@ export function subscribeToFindMany<const S extends AnyNoContext>(
390384 } ) ;
391385 }
392386
387+ const subscribe = ( callback : ( ) => void ) => {
388+ const query = decodedEntitiesCache . get ( typeName ) ?. queries . get ( queryKey ) ;
389+ if ( query ?. listeners ) {
390+ query . listeners . push ( callback ) ;
391+ }
392+ return ( ) => {
393+ const query = decodedEntitiesCache . get ( typeName ) ?. queries . get ( queryKey ) ;
394+ if ( query ?. listeners ) {
395+ query . listeners = query ?. listeners ?. filter ( ( cachedListener ) => cachedListener !== callback ) ;
396+ }
397+
398+ documentChangeListener . subscribedQueriesCount -- ;
399+ if ( documentChangeListener . subscribedQueriesCount === 0 ) {
400+ documentChangeListener . unsubscribe ?.( ) ;
401+ documentChangeListener . unsubscribe = undefined ;
402+ }
403+ } ;
404+ } ;
405+
393406 if ( documentChangeListener . subscribedQueriesCount === 0 ) {
394407 documentChangeListener . unsubscribe = subscribeToDocumentChanges ( handle ) ;
395408 }
396409 documentChangeListener . subscribedQueriesCount ++ ;
397410
398- return { listener , getEntities, unsubscribe } ;
411+ return { subscribe , getEntities } ;
399412}
400413
401414/**
0 commit comments