Skip to content

Commit 266deb6

Browse files
committed
Switch to using reducer rather than multiple useState
1 parent 17fed72 commit 266deb6

File tree

10 files changed

+238
-105
lines changed

10 files changed

+238
-105
lines changed

auth/useAuthState.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
import { auth, User } from 'firebase';
22
import { useEffect } from 'react';
3-
import { useDataLoader } from '../util';
3+
import { useLoadingValue } from '../util';
44

55
export type AuthStateHook = {
66
user?: firebase.User;
77
initialising: boolean;
88
};
99

1010
export default (auth: auth.Auth): AuthStateHook => {
11-
const { loading, setValue, value } = useDataLoader<User>();
11+
const { loading, setValue, value } = useLoadingValue<User>();
1212

13-
useEffect(() => {
14-
const listener = auth.onAuthStateChanged(setValue);
13+
useEffect(
14+
() => {
15+
const listener = auth.onAuthStateChanged(setValue);
1516

16-
return () => {
17-
listener();
18-
};
19-
}, []);
17+
return () => {
18+
listener();
19+
};
20+
},
21+
[auth]
22+
);
2023

2124
return {
2225
initialising: loading,

database/useList.ts

Lines changed: 141 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { database } from 'firebase';
2-
import { useEffect, useRef, useState } from 'react';
2+
import { useEffect, useReducer, useRef, useState } from 'react';
33

44
export type ListHook = {
55
error?: Object;
@@ -12,72 +12,139 @@ type KeyValueState = {
1212
values: database.DataSnapshot[];
1313
};
1414

15+
type ReducerState = {
16+
error?: object;
17+
loading: boolean;
18+
value: KeyValueState;
19+
};
20+
21+
type AddAction = {
22+
type: 'add';
23+
previousKey?: string | null;
24+
snapshot: database.DataSnapshot | null;
25+
};
26+
type ChangeAction = {
27+
type: 'change';
28+
snapshot: database.DataSnapshot | null;
29+
};
30+
type ErrorAction = { type: 'error'; error: object };
31+
type MoveAction = {
32+
type: 'move';
33+
previousKey?: string | null;
34+
snapshot: database.DataSnapshot | null;
35+
};
36+
type RemoveAction = {
37+
type: 'remove';
38+
snapshot: database.DataSnapshot | null;
39+
};
40+
type ResetAction = { type: 'reset' };
41+
type ValueAction = { type: 'value'; value: any };
42+
type ReducerAction =
43+
| AddAction
44+
| ChangeAction
45+
| ErrorAction
46+
| MoveAction
47+
| RemoveAction
48+
| ResetAction
49+
| ValueAction;
50+
51+
const initialState: ReducerState = {
52+
loading: true,
53+
value: {
54+
keys: [],
55+
values: [],
56+
},
57+
};
58+
59+
const reducer = (state: ReducerState, action: ReducerAction): ReducerState => {
60+
switch (action.type) {
61+
case 'add':
62+
if (!action.snapshot) {
63+
return state;
64+
}
65+
return {
66+
...state,
67+
value: addChild(state.value, action.snapshot, action.previousKey),
68+
};
69+
case 'change':
70+
if (!action.snapshot) {
71+
return state;
72+
}
73+
return {
74+
...state,
75+
value: changeChild(state.value, action.snapshot),
76+
};
77+
case 'error':
78+
return {
79+
...state,
80+
error: action.error,
81+
loading: false,
82+
};
83+
case 'move':
84+
if (!action.snapshot) {
85+
return state;
86+
}
87+
return {
88+
...state,
89+
value: moveChild(state.value, action.snapshot, action.previousKey),
90+
};
91+
case 'remove':
92+
if (!action.snapshot) {
93+
return state;
94+
}
95+
return {
96+
...state,
97+
value: removeChild(state.value, action.snapshot),
98+
};
99+
case 'reset':
100+
return initialState;
101+
case 'value':
102+
return {
103+
...state,
104+
loading: false,
105+
value: action.value,
106+
};
107+
default:
108+
return state;
109+
}
110+
};
111+
15112
export default (query: database.Query): ListHook => {
113+
const [state, dispatch] = useReducer(reducer, initialState);
114+
16115
const [error, setError] = useState(false);
17116
const [loading, setLoading] = useState(true);
18117
// Combine keys and values in a single state hook to allow them to be manipulated together
19118
const [{ values }, setKeysValues] = useState({ keys: [], values: [] });
20119
// Set a ref for the query to make sure that `useEffect` doesn't run
21120
// every time this renders
22121
const queryRef = useRef(query);
23-
// If the query has changed, then
122+
// If the query has changed, then reset the state
24123
if (!query.isEqual(queryRef.current)) {
25124
queryRef.current = query;
26-
setError(false);
27-
setLoading(true);
28-
setKeysValues({ keys: [], values: [] });
125+
dispatch({ type: 'reset' });
29126
}
30127

31128
const onChildAdded = (
32129
snapshot: database.DataSnapshot | null,
33130
previousKey?: string | null
34131
) => {
35-
setKeysValues((prevKeyValueState: KeyValueState) => {
36-
return snapshot
37-
? addChild(prevKeyValueState, snapshot, previousKey)
38-
: prevKeyValueState;
39-
});
132+
dispatch({ type: 'add', previousKey, snapshot });
40133
};
41134

42135
const onChildChanged = (snapshot: database.DataSnapshot | null) => {
43-
setKeysValues((prevKeyValueState: KeyValueState) => {
44-
if (!snapshot || !snapshot.key) {
45-
return prevKeyValueState;
46-
}
47-
48-
const index = prevKeyValueState.keys.indexOf(snapshot.key);
49-
return {
50-
...prevKeyValueState,
51-
values: [
52-
...prevKeyValueState.values.slice(0, index),
53-
snapshot,
54-
...prevKeyValueState.values.slice(index + 1),
55-
],
56-
};
57-
});
136+
dispatch({ type: 'change', snapshot });
58137
};
59138

60139
const onChildMoved = (
61140
snapshot: database.DataSnapshot | null,
62141
previousKey?: string | null
63142
) => {
64-
setKeysValues((prevKeyValueState: KeyValueState) => {
65-
if (!snapshot) {
66-
return prevKeyValueState;
67-
}
68-
// Remove the child from it's previous location
69-
const tempKeyValueState = removeChild(prevKeyValueState, snapshot);
70-
// Add the child into it's new location
71-
return addChild(tempKeyValueState, snapshot, previousKey);
72-
});
143+
dispatch({ type: 'move', previousKey, snapshot });
73144
};
74145

75146
const onChildRemoved = (snapshot: database.DataSnapshot | null) => {
76-
setKeysValues((prevKeyValueState: KeyValueState) => {
77-
return snapshot
78-
? removeChild(prevKeyValueState, snapshot)
79-
: prevKeyValueState;
80-
});
147+
dispatch({ type: 'remove', snapshot });
81148
};
82149

83150
useEffect(
@@ -117,15 +184,15 @@ export default (query: database.Query): ListHook => {
117184
};
118185

119186
const addChild = (
120-
keyValueState: KeyValueState,
187+
currentState: KeyValueState,
121188
snapshot: firebase.database.DataSnapshot,
122189
previousKey?: string | null
123190
): KeyValueState => {
124191
if (!snapshot.key) {
125-
return keyValueState;
192+
return currentState;
126193
}
127194

128-
const { keys, values } = keyValueState;
195+
const { keys, values } = currentState;
129196
if (!previousKey) {
130197
// The child has been added to the start of the list
131198
return {
@@ -146,18 +213,47 @@ const addChild = (
146213
};
147214
};
148215

216+
const changeChild = (
217+
currentState: KeyValueState,
218+
snapshot: firebase.database.DataSnapshot
219+
): KeyValueState => {
220+
if (!snapshot.key) {
221+
return currentState;
222+
}
223+
const index = currentState.keys.indexOf(snapshot.key);
224+
return {
225+
...currentState,
226+
values: [
227+
...currentState.values.slice(0, index),
228+
snapshot,
229+
...currentState.values.slice(index + 1),
230+
],
231+
};
232+
};
233+
149234
const removeChild = (
150-
keyValueState: KeyValueState,
235+
currentState: KeyValueState,
151236
snapshot: firebase.database.DataSnapshot
152237
): KeyValueState => {
153238
if (!snapshot.key) {
154-
return keyValueState;
239+
return currentState;
155240
}
156241

157-
const { keys, values } = keyValueState;
242+
const { keys, values } = currentState;
158243
const index = keys.indexOf(snapshot.key);
159244
return {
160245
keys: [...keys.slice(0, index), ...keys.slice(index + 1)],
161246
values: [...values.slice(0, index), ...values.slice(index + 1)],
162247
};
163248
};
249+
250+
const moveChild = (
251+
currentState: KeyValueState,
252+
snapshot: firebase.database.DataSnapshot,
253+
previousKey?: string | null
254+
): KeyValueState => {
255+
// Remove the child from it's previous location
256+
const tempValue = removeChild(currentState, snapshot);
257+
// Add the child into it's new location
258+
return addChild(tempValue, snapshot, previousKey);
259+
};

database/useObject.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { database } from 'firebase';
22
import { useEffect, useRef } from 'react';
3-
import { useDataLoader } from '../util';
3+
import { useLoadingValue } from '../util';
44

55
export type ObjectHook = {
66
error?: object;
@@ -9,7 +9,7 @@ export type ObjectHook = {
99
};
1010

1111
export default (query: database.Query): ObjectHook => {
12-
const { error, loading, reset, setError, setValue, value } = useDataLoader<
12+
const { error, loading, reset, setError, setValue, value } = useLoadingValue<
1313
database.DataSnapshot
1414
>();
1515
// Set a ref for the query to make sure that `useEffect` doesn't run

firestore/useCollection.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { firestore } from 'firebase';
22
import { useEffect, useRef } from 'react';
3-
import { useDataLoader } from '../util';
3+
import { useLoadingValue } from '../util';
44

55
export type CollectionHook = {
66
error?: object;
@@ -12,7 +12,7 @@ export default (
1212
query: firestore.Query,
1313
options?: firestore.SnapshotListenOptions
1414
): CollectionHook => {
15-
const { error, loading, reset, setError, setValue, value } = useDataLoader<
15+
const { error, loading, reset, setError, setValue, value } = useLoadingValue<
1616
firestore.QuerySnapshot
1717
>();
1818
// Set a ref for the query to make sure that `useEffect` doesn't run

firestore/useDocument.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { firestore } from 'firebase';
22
import { useEffect, useRef } from 'react';
3-
import { useDataLoader } from '../util';
3+
import { useLoadingValue } from '../util';
44

55
export type DocumentHook = {
66
error?: object;
@@ -12,7 +12,7 @@ export default (
1212
doc: firestore.DocumentReference,
1313
options?: firestore.SnapshotListenOptions
1414
): DocumentHook => {
15-
const { error, loading, reset, setError, setValue, value } = useDataLoader<
15+
const { error, loading, reset, setError, setValue, value } = useLoadingValue<
1616
firestore.DocumentSnapshot
1717
>();
1818
// Set a ref for the query to make sure that `useEffect` doesn't run

storage/useDownloadURL.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { storage } from 'firebase';
22
import { useEffect, useRef } from 'react';
3-
import { useDataLoader } from '../util';
3+
import { useLoadingValue } from '../util';
44

55
export type DownloadURLHook = {
66
error?: object;
@@ -9,7 +9,7 @@ export type DownloadURLHook = {
99
};
1010

1111
export default (ref: storage.Reference): DownloadURLHook => {
12-
const { error, loading, reset, setError, setValue, value } = useDataLoader<
12+
const { error, loading, reset, setError, setValue, value } = useLoadingValue<
1313
string
1414
>();
1515
// Set a ref for the query to make sure that `useEffect` doesn't run

util/comparators.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

util/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
export * from './comparators';
2-
export { default as useDataLoader } from './useDataLoader';
1+
export { default as useLoadingValue } from './useLoadingValue';

0 commit comments

Comments
 (0)