Skip to content

Commit 2bdb336

Browse files
committed
Return memorized arrays from hooks
This fixes an issue where a user may end up getting a new array with the same entries from these hooks. This was an issue if a user wrote code like: ``` const queryResults = useCollection(...) ``` Every time the component rendered, `useCollection` would return a new Array, even if none of the memoized value/error/loading state had changed.
1 parent 048dcb4 commit 2bdb336

File tree

7 files changed

+85
-23
lines changed

7 files changed

+85
-23
lines changed

auth/useAuthState.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { auth, User } from 'firebase';
2-
import { useEffect } from 'react';
2+
import { useEffect, useMemo } from 'react';
33
import { LoadingHook, useLoadingValue } from '../util';
44

55
export type AuthStateHook = LoadingHook<User, auth.Error>;
@@ -21,5 +21,9 @@ export default (auth: auth.Auth): AuthStateHook => {
2121
[auth]
2222
);
2323

24-
return [value, loading, error];
24+
const resArray:AuthStateHook = [value, loading, error]
25+
return useMemo<AuthStateHook>(
26+
() => resArray,
27+
resArray,
28+
);
2529
};

database/useList.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,25 @@ export const useList = (query?: database.Query | null): ListHook => {
6464
};
6565
}, [ref.current]);
6666

67-
return [state.value.values, state.loading, state.error];
67+
const resArray: ListHook = [state.value.values, state.loading, state.error];
68+
return useMemo(
69+
() => resArray,
70+
resArray,
71+
);
6872
};
6973

7074
export const useListKeys = (query?: database.Query | null): ListKeysHook => {
7175
const [value, loading, error] = useList(query);
72-
return [
76+
const resArray: ListKeysHook = [
7377
value ? value.map(snapshot => snapshot.key as string) : undefined,
7478
loading,
7579
error,
7680
];
81+
82+
return useMemo(
83+
() => resArray,
84+
resArray,
85+
);
7786
};
7887

7988
export const useListVals = <T>(
@@ -92,5 +101,10 @@ export const useListVals = <T>(
92101
: undefined,
93102
[snapshots, options && options.keyField]
94103
);
95-
return [values, loading, error];
104+
105+
const resArray: ListValsHook<T> = [values, loading, error];
106+
return useMemo(
107+
() => resArray,
108+
resArray,
109+
);
96110
};

database/useObject.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ export const useObject = (query?: database.Query | null): ObjectHook => {
3030
[ref.current]
3131
);
3232

33-
return [value, loading, error];
33+
const resArray: ObjectHook = [value, loading, error];
34+
return useMemo(
35+
() => resArray,
36+
resArray,
37+
);
3438
};
3539

3640
export const useObjectVal = <T>(
@@ -47,5 +51,10 @@ export const useObjectVal = <T>(
4751
: undefined,
4852
[snapshot, options && options.keyField]
4953
);
50-
return [value, loading, error];
54+
55+
const resArray: ObjectValHook<T> = [value, loading, error];
56+
return useMemo(
57+
() => resArray,
58+
resArray,
59+
);
5160
};

firestore/useCollection.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@ export const useCollection = (
4040
[ref.current]
4141
);
4242

43-
return [value, loading, error];
43+
const resArray: CollectionHook = [value, loading, error];
44+
return useMemo(
45+
() => resArray,
46+
resArray,
47+
);
4448
};
4549

4650
export const useCollectionData = <T>(
@@ -64,5 +68,10 @@ export const useCollectionData = <T>(
6468
: undefined) as T[],
6569
[snapshot, idField]
6670
);
67-
return [values, loading, error];
71+
72+
const resArray: CollectionDataHook<T> = [values, loading, error]
73+
return useMemo(
74+
() => resArray,
75+
resArray,
76+
);
6877
};

firestore/useCollectionOnce.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { firestore } from 'firebase';
2-
import { useEffect } from 'react';
2+
import { useEffect, useMemo } from 'react';
33
import { snapshotToData } from './helpers';
44
import { LoadingHook, useIsEqualRef, useLoadingValue } from '../util';
55

@@ -32,7 +32,11 @@ export const useCollectionOnce = (
3232
[ref.current]
3333
);
3434

35-
return [value, loading, error];
35+
const resArray: CollectionOnceHook = [value, loading, error];
36+
return useMemo(
37+
() => resArray,
38+
resArray,
39+
);
3640
};
3741

3842
export const useCollectionDataOnce = <T>(
@@ -45,11 +49,15 @@ export const useCollectionDataOnce = <T>(
4549
const idField = options ? options.idField : undefined;
4650
const getOptions = options ? options.getOptions : undefined;
4751
const [value, loading, error] = useCollectionOnce(query, { getOptions });
48-
return [
52+
const resArray: CollectionDataOnceHook<T> = [
4953
(value
5054
? value.docs.map(doc => snapshotToData(doc, idField))
5155
: undefined) as T[],
5256
loading,
5357
error,
5458
];
59+
return useMemo(
60+
() => resArray,
61+
resArray,
62+
);
5563
};

firestore/useDocument.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@ export const useDocument = (
4040
[ref.current]
4141
);
4242

43-
return [value, loading, error];
43+
const resArray: DocumentHook = [value, loading, error];
44+
return useMemo(
45+
() => resArray,
46+
resArray,
47+
);
4448
};
4549

4650
export const useDocumentData = <T>(
@@ -61,5 +65,10 @@ export const useDocumentData = <T>(
6165
() => (snapshot ? snapshotToData(snapshot, idField) : undefined) as T,
6266
[snapshot, idField]
6367
);
64-
return [value, loading, error];
68+
69+
const resArray: DocumentDataHook<T> = [value, loading, error];
70+
return useMemo(
71+
() => resArray,
72+
resArray,
73+
);
6574
};

util/useLoadingValue.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useReducer } from 'react';
1+
import { useMemo, useReducer } from 'react';
22

33
export type LoadingValue<T, E> = {
44
error?: E;
@@ -73,12 +73,21 @@ export default <T, E>(getDefaultValue?: () => T | null): LoadingValue<T, E> => {
7373
dispatch({ type: 'value', value });
7474
};
7575

76-
return {
77-
error: state.error,
78-
loading: state.loading,
79-
reset,
80-
setError,
81-
setValue,
82-
value: state.value,
83-
};
76+
return useMemo(
77+
() => ({
78+
error: state.error,
79+
loading: state.loading,
80+
reset,
81+
setError,
82+
setValue,
83+
value: state.value,
84+
}), [
85+
state.error,
86+
state.loading,
87+
reset,
88+
setError,
89+
setValue,
90+
state.value,
91+
]
92+
);
8493
};

0 commit comments

Comments
 (0)