@@ -14,58 +14,100 @@ export type ListValsHook<T> = LoadingHook<T[], firebase.FirebaseError>;
1414export const useList = ( query ?: firebase . database . Query | null ) : ListHook => {
1515 const [ state , dispatch ] = useListReducer ( ) ;
1616
17- const ref = useIsEqualRef ( query , ( ) => dispatch ( { type : 'reset' } ) ) ;
18-
19- const onChildAdded = (
20- snapshot : firebase . database . DataSnapshot | null ,
21- previousKey ?: string | null
22- ) => {
23- dispatch ( { type : 'add' , previousKey, snapshot } ) ;
24- } ;
25-
26- const onChildChanged = ( snapshot : firebase . database . DataSnapshot | null ) => {
27- dispatch ( { type : 'change' , snapshot } ) ;
28- } ;
29-
30- const onChildMoved = (
31- snapshot : firebase . database . DataSnapshot | null ,
32- previousKey ?: string | null
33- ) => {
34- dispatch ( { type : 'move' , previousKey, snapshot } ) ;
35- } ;
36-
37- const onChildRemoved = ( snapshot : firebase . database . DataSnapshot | null ) => {
38- dispatch ( { type : 'remove' , snapshot } ) ;
39- } ;
40-
41- const onError = ( error : firebase . FirebaseError ) => {
42- dispatch ( { type : 'error' , error } ) ;
43- } ;
44-
45- const onValue = ( ) => {
46- dispatch ( { type : 'value' } ) ;
47- } ;
17+ const queryRef = useIsEqualRef ( query , ( ) => dispatch ( { type : 'reset' } ) ) ;
18+ const ref : firebase . database . Query | null | undefined = queryRef . current ;
4819
4920 useEffect ( ( ) => {
50- const query : firebase . database . Query | null | undefined = ref . current ;
51- if ( ! query ) {
21+ if ( ! ref ) {
5222 dispatch ( { type : 'empty' } ) ;
5323 return ;
5424 }
55- // This is here to indicate that all the data has been successfully received
56- query . once ( 'value' , onValue , onError ) ;
57- query . on ( 'child_added' , onChildAdded , onError ) ;
58- query . on ( 'child_changed' , onChildChanged , onError ) ;
59- query . on ( 'child_moved' , onChildMoved , onError ) ;
60- query . on ( 'child_removed' , onChildRemoved , onError ) ;
25+
26+ const onChildAdded = (
27+ snapshot : firebase . database . DataSnapshot | null ,
28+ previousKey ?: string | null
29+ ) => {
30+ dispatch ( { type : 'add' , previousKey, snapshot } ) ;
31+ } ;
32+
33+ const onChildChanged = (
34+ snapshot : firebase . database . DataSnapshot | null
35+ ) => {
36+ dispatch ( { type : 'change' , snapshot } ) ;
37+ } ;
38+
39+ const onChildMoved = (
40+ snapshot : firebase . database . DataSnapshot | null ,
41+ previousKey ?: string | null
42+ ) => {
43+ dispatch ( { type : 'move' , previousKey, snapshot } ) ;
44+ } ;
45+
46+ const onChildRemoved = (
47+ snapshot : firebase . database . DataSnapshot | null
48+ ) => {
49+ dispatch ( { type : 'remove' , snapshot } ) ;
50+ } ;
51+
52+ const onError = ( error : firebase . FirebaseError ) => {
53+ dispatch ( { type : 'error' , error } ) ;
54+ } ;
55+
56+ const onValue = ( snapshots : firebase . database . DataSnapshot [ ] | null ) => {
57+ dispatch ( { type : 'value' , snapshots } ) ;
58+ } ;
59+
60+ let childAddedHandler : ReturnType < typeof ref . on > | undefined ;
61+ const children : firebase . database . DataSnapshot [ ] = [ ] ;
62+ const onInitialLoad = ( snapshot : firebase . database . DataSnapshot ) => {
63+ let childrenToProcess = Object . keys ( snapshot . val ( ) ) . length ;
64+
65+ const onChildAddedWithoutInitialLoad = (
66+ addedChild : firebase . database . DataSnapshot ,
67+ previousKey ?: string | null
68+ ) => {
69+ // process the first batch of children all at once
70+ if ( childrenToProcess > 0 ) {
71+ childrenToProcess -- ;
72+ children . push ( addedChild ) ;
73+
74+ if ( childrenToProcess === 0 ) {
75+ onValue ( children ) ;
76+ }
77+
78+ return ;
79+ }
80+
81+ onChildAdded ( snapshot , previousKey ) ;
82+ } ;
83+
84+ childAddedHandler = ref . on (
85+ 'child_added' ,
86+ onChildAddedWithoutInitialLoad ,
87+ onError
88+ ) ;
89+ } ;
90+
91+ ref . once ( 'value' , onInitialLoad , onError ) ;
92+ const childChangedHandler = ref . on (
93+ 'child_changed' ,
94+ onChildChanged ,
95+ onError
96+ ) ;
97+ const childMovedHandler = ref . on ( 'child_moved' , onChildMoved , onError ) ;
98+ const childRemovedHandler = ref . on (
99+ 'child_removed' ,
100+ onChildRemoved ,
101+ onError
102+ ) ;
61103
62104 return ( ) => {
63- query . off ( 'child_added' , onChildAdded ) ;
64- query . off ( 'child_changed' , onChildChanged ) ;
65- query . off ( 'child_moved' , onChildMoved ) ;
66- query . off ( 'child_removed' , onChildRemoved ) ;
105+ ref . off ( 'child_added' , childAddedHandler ) ;
106+ ref . off ( 'child_changed' , childChangedHandler ) ;
107+ ref . off ( 'child_moved' , childMovedHandler ) ;
108+ ref . off ( 'child_removed' , childRemovedHandler ) ;
67109 } ;
68- } , [ ref . current ] ) ;
110+ } , [ dispatch , ref ] ) ;
69111
70112 const resArray : ListHook = [ state . value . values , state . loading , state . error ] ;
71113 return useMemo ( ( ) => resArray , resArray ) ;
0 commit comments