1- import { useEffect , useState } from 'react' ;
1+ import { useCallback , useMemo , useSyncExternalStore } from 'react' ;
22
33import type { StateStore } from 'stream-chat' ;
44
5+ // eslint-disable-next-line @typescript-eslint/no-empty-function
6+ const noop = ( ) => { } ;
7+
58export function useStateStore <
69 T extends Record < string , unknown > ,
710 O extends Readonly < Record < string , unknown > | Readonly < unknown [ ] > >
@@ -14,18 +17,31 @@ export function useStateStore<
1417 T extends Record < string , unknown > ,
1518 O extends Readonly < Record < string , unknown > | Readonly < unknown [ ] > >
1619> ( store : StateStore < T > | undefined , selector : ( v : T ) => O ) {
17- const [ state , setState ] = useState < O | undefined > ( ( ) => {
18- if ( ! store ) return undefined ;
19- return selector ( store . getLatestValue ( ) ) ;
20- } ) ;
20+ const wrappedSubscription = useCallback (
21+ ( onStoreChange : ( ) => void ) => {
22+ const unsubscribe = store ?. subscribeWithSelector ( selector , onStoreChange ) ;
23+ return unsubscribe ?? noop ;
24+ } ,
25+ [ store , selector ] ,
26+ ) ;
27+
28+ const wrappedSnapshot = useMemo ( ( ) => {
29+ let cached : [ T , O ] ;
30+ return ( ) => {
31+ const current = store ?. getLatestValue ( ) ;
2132
22- useEffect ( ( ) => {
23- if ( ! store ) return ;
33+ if ( ! current ) return undefined ;
2434
25- const unsubscribe = store . subscribeWithSelector ( selector , setState ) ;
35+ if ( ! cached || cached [ 0 ] !== current ) {
36+ cached = [ current , selector ( current ) ] ;
37+ return cached [ 1 ] ;
38+ }
2639
27- return unsubscribe ;
40+ return cached [ 1 ] ;
41+ } ;
2842 } , [ store , selector ] ) ;
2943
44+ const state = useSyncExternalStore ( wrappedSubscription , wrappedSnapshot ) ;
45+
3046 return state ;
3147}
0 commit comments