Skip to content

Commit 032676d

Browse files
authored
v0.2.8
* fix(errorsReducer): correctly update on `LISTENER_ERROR` dispatch - [RRF #386](prescottprue/react-redux-firebase#386) * feat(tests): more unit tests added for `orderedReducer` (including one covering update of existing document within subcollection) * fix(types): typescript typings updated (non existent constants removed)
2 parents ba627c0 + 8a71785 commit 032676d

12 files changed

+467
-123
lines changed

index.d.ts

+79-82
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,85 @@
1-
/** Declaration file generated by dts-gen */
2-
1+
/**
2+
* Action types used within actions dispatched internally. These action types
3+
* can be manually dispatched to update state.
4+
*/
35
export const actionTypes: {
4-
AUTHENTICATION_INIT_FINISHED: string;
5-
AUTHENTICATION_INIT_STARTED: string;
6-
AUTH_UPDATE_ERROR: string;
7-
AUTH_UPDATE_START: string;
8-
AUTH_UPDATE_SUCCESS: string;
9-
EMAIL_UPDATE_ERROR: string;
10-
EMAIL_UPDATE_START: string;
11-
EMAIL_UPDATE_SUCCESS: string;
12-
ERROR: string;
13-
FILE_DELETE_COMPLETE: string;
14-
FILE_DELETE_ERROR: string;
15-
FILE_DELETE_START: string;
16-
FILE_UPLOAD_COMPLETE: string;
17-
FILE_UPLOAD_ERROR: string;
18-
FILE_UPLOAD_PROGRESS: string;
19-
FILE_UPLOAD_START: string;
20-
LOGIN: string;
21-
LOGIN_ERROR: string;
22-
LOGOUT: string;
23-
NO_VALUE: string;
24-
PROFILE_UPDATE_ERROR: string;
25-
PROFILE_UPDATE_START: string;
26-
PROFILE_UPDATE_SUCCESS: string;
27-
SET: string;
28-
SET_PROFILE: string;
29-
START: string;
30-
UNAUTHORIZED_ERROR: string;
31-
UNSET_LISTENER: string;
6+
START: string;
7+
ERROR: string;
8+
CLEAR_DATA: string;
9+
CLEAR_ERROR: string;
10+
CLEAR_ERRORS: string;
11+
SET_LISTENER: string;
12+
UNSET_LISTENER: string;
13+
GET_REQUEST: string;
14+
GET_SUCCESS: string;
15+
GET_FAILURE: string;
16+
SET_REQUEST: string;
17+
SET_SUCCESS: string;
18+
SET_FAILURE: string;
19+
ADD_REQUEST: string;
20+
ADD_SUCCESS: string;
21+
ADD_FAILURE: string;
22+
UPDATE_REQUEST: string;
23+
UPDATE_SUCCESS: string;
24+
UPDATE_FAILURE: string;
25+
DELETE_REQUEST: string;
26+
DELETE_SUCCESS: string;
27+
DELETE_FAILURE: string;
28+
ATTACH_LISTENER: string;
29+
LISTENER_RESPONSE: string;
30+
LISTENER_ERROR: string;
31+
ON_SNAPSHOT_REQUEST: string;
32+
ON_SNAPSHOT_SUCCESS: string;
33+
ON_SNAPSHOT_FAILURE: string;
3234
};
3335

36+
/**
37+
* Constants used within redux-firetore. Includes actionTypes, actionsPrefix,
38+
* and default config.
39+
*/
3440
export const constants: {
35-
actionTypes: {
36-
AUTHENTICATION_INIT_FINISHED: string;
37-
AUTHENTICATION_INIT_STARTED: string;
38-
AUTH_UPDATE_ERROR: string;
39-
AUTH_UPDATE_START: string;
40-
AUTH_UPDATE_SUCCESS: string;
41-
EMAIL_UPDATE_ERROR: string;
42-
EMAIL_UPDATE_START: string;
43-
EMAIL_UPDATE_SUCCESS: string;
44-
ERROR: string;
45-
FILE_DELETE_COMPLETE: string;
46-
FILE_DELETE_ERROR: string;
47-
FILE_DELETE_START: string;
48-
FILE_UPLOAD_COMPLETE: string;
49-
FILE_UPLOAD_ERROR: string;
50-
FILE_UPLOAD_PROGRESS: string;
51-
FILE_UPLOAD_START: string;
52-
LOGIN: string;
53-
LOGIN_ERROR: string;
54-
LOGOUT: string;
55-
NO_VALUE: string;
56-
PROFILE_UPDATE_ERROR: string;
57-
PROFILE_UPDATE_START: string;
58-
PROFILE_UPDATE_SUCCESS: string;
59-
SET: string;
60-
SET_PROFILE: string;
61-
START: string;
62-
UNAUTHORIZED_ERROR: string;
63-
UNSET_LISTENER: string;
64-
};
65-
defaultConfig: {
66-
autoPopulateProfile: boolean;
67-
dispatchOnUnsetListener: boolean;
68-
enableEmptyAuthChanges: boolean;
69-
enableLogging: boolean;
70-
enableRedirectHandling: boolean;
71-
setProfilePopulateResults: boolean;
72-
updateProfileOnLogin: boolean;
73-
userProfile: any;
74-
};
75-
defaultInitProps: string[];
76-
defaultJWTProps: string[];
77-
metaParams: string[];
78-
paramSplitChar: string;
79-
supportedAuthProviders: string[];
41+
actionTypes: {
42+
START: string;
43+
ERROR: string;
44+
CLEAR_DATA: string;
45+
CLEAR_ERROR: string;
46+
CLEAR_ERRORS: string;
47+
SET_LISTENER: string;
48+
UNSET_LISTENER: string;
49+
GET_REQUEST: string;
50+
GET_SUCCESS: string;
51+
GET_FAILURE: string;
52+
SET_REQUEST: string;
53+
SET_SUCCESS: string;
54+
SET_FAILURE: string;
55+
ADD_REQUEST: string;
56+
ADD_SUCCESS: string;
57+
ADD_FAILURE: string;
58+
UPDATE_REQUEST: string;
59+
UPDATE_SUCCESS: string;
60+
UPDATE_FAILURE: string;
61+
DELETE_REQUEST: string;
62+
DELETE_SUCCESS: string;
63+
DELETE_FAILURE: string;
64+
ATTACH_LISTENER: string;
65+
LISTENER_RESPONSE: string;
66+
LISTENER_ERROR: string;
67+
ON_SNAPSHOT_REQUEST: string;
68+
ON_SNAPSHOT_SUCCESS: string;
69+
ON_SNAPSHOT_FAILURE: string;
70+
};
71+
actionsPrefix: string;
72+
defaultConfig: {
73+
enableLogging: boolean;
74+
enhancerNamespace: string;
75+
helpersNamespace: string;
76+
preserveOnListenerError: object;
77+
};
8078
};
8179

8280
/**
83-
* A redux store enhancer that adds store.firebase
81+
* A redux store enhancer that adds store.firebase (passed to React component
82+
* context through react-redux's <Provider>).
8483
*/
8584
export function reduxFirestore(firebaseInstance: object, otherConfig: object): any;
8685

@@ -94,16 +93,14 @@ export function getFirestore(firebaseInstance: object, otherConfig: object): any
9493
* A redux store reducer for Firestore state
9594
*/
9695
export namespace firestoreReducer {
97-
const prototype: {
98-
};
99-
96+
const prototype: {
97+
};
10098
}
10199

102100
/**
103101
* A redux store reducer for Firestore state
104102
*/
105103
export namespace reduxFirestore {
106-
const prototype: {
107-
};
108-
104+
const prototype: {
105+
};
109106
}

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "redux-firestore",
3-
"version": "0.2.7",
3+
"version": "0.2.8",
44
"description": "Redux bindings for Firestore.",
55
"main": "lib/index.js",
66
"module": "es/index.js",

src/actions/firestore.js

+1
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ export const setListener = (
160160
errorCb,
161161
) => {
162162
const meta = getQueryConfig(queryOpts);
163+
// Create listener
163164
const unsubscribe = firestoreRef(firebase, dispatch, meta).onSnapshot(
164165
docData => {
165166
dispatch({

src/reducers/dataReducer.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export default function dataReducer(state = {}, action) {
3838
const data = docName ? get(payload.data, docName) : payload.data;
3939
// Get previous data at path to check for existence
4040
const previousData = get(state, pathFromMeta(meta));
41-
// Only merge if data does not already exist or if meta contains subcollections
41+
// Set data (without merging) if no previous data exists or if there are subcollections
4242
if (!previousData || meta.subcollections) {
4343
// Set data to state immutabily (lodash/fp's setWith creates copy)
4444
return setWith(Object, pathFromMeta(meta), data, state);
@@ -53,6 +53,7 @@ export default function dataReducer(state = {}, action) {
5353
return pick(state, action.preserve); // pick returns a new object
5454
}
5555
return {};
56+
// TODO: LISTENER_ERROR that sets null in a way that is configurable (v0.3.0)
5657
default:
5758
return state;
5859
}

src/reducers/errorsReducer.js

+11-24
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
import { actionTypes } from '../constants';
22
import { combineReducers, pathFromMeta } from '../utils/reducers';
33

4-
const {
5-
CLEAR_ERRORS,
6-
CLEAR_ERROR,
7-
LOGIN_ERROR,
8-
LISTENER_ERROR,
9-
ERROR,
10-
} = actionTypes;
4+
const { CLEAR_ERRORS, CLEAR_ERROR, LISTENER_ERROR, ERROR } = actionTypes;
115

126
/**
137
* Reducer for errors state. Changed by `ERROR`
@@ -18,17 +12,17 @@ const {
1812
* @return {Object} Profile state after reduction
1913
*/
2014
const errorsAllIds = (state = [], { meta, type }) => {
21-
if (!meta || !meta.id) {
22-
return state;
23-
}
2415
switch (type) {
25-
case LOGIN_ERROR:
16+
case LISTENER_ERROR:
2617
case ERROR:
27-
return [...state, meta.id];
18+
if (state.indexOf(pathFromMeta(meta)) !== -1) {
19+
return state;
20+
}
21+
return [...state, pathFromMeta(meta)];
2822
case CLEAR_ERRORS:
2923
return [];
3024
case CLEAR_ERROR:
31-
return state.filter(lId => lId !== meta.id);
25+
return state.filter(lId => lId !== pathFromMeta(meta));
3226
default:
3327
return state;
3428
}
@@ -43,25 +37,18 @@ const errorsAllIds = (state = [], { meta, type }) => {
4337
* @return {Object} Profile state after reduction
4438
*/
4539
const errorsByQuery = (state = {}, { meta, payload, type }) => {
46-
if (!meta || !meta.id) {
47-
return state;
48-
}
4940
switch (type) {
50-
case LOGIN_ERROR:
5141
case ERROR:
52-
return {
53-
...state,
54-
[meta.id]: payload,
55-
};
5642
case LISTENER_ERROR:
5743
return {
5844
...state,
5945
[pathFromMeta(meta)]: payload,
6046
};
61-
case CLEAR_ERRORS:
62-
return [];
6347
case CLEAR_ERROR:
64-
return state.filter(lId => lId !== payload.id);
48+
return {
49+
...state,
50+
[pathFromMeta(meta)]: null,
51+
};
6552
default:
6653
return state;
6754
}

src/reducers/listenersReducer.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ const listenersById = (state = {}, { type, path, payload }) => {
2929
};
3030

3131
/**
32-
* Reducer for listeners state. Changed by `ERROR`
33-
* and `LOGOUT` actions.
32+
* Reducer for listeners state. Changed by `ERROR` and `LOGOUT` actions.
3433
* @param {Object} [state=[]] - Current authError redux state
3534
* @param {Object} action - Object containing the action that was dispatched
3635
* @param {String} action.type - Type of action that was dispatched

src/reducers/orderedReducer.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { pick, first } from 'lodash';
22
import { actionTypes } from '../constants';
3-
import { updateItemInArray, updateObject } from '../utils/reducers';
3+
import { updateItemInArray } from '../utils/reducers';
44

55
const { GET_SUCCESS, LISTENER_RESPONSE, CLEAR_DATA } = actionTypes;
66

@@ -27,7 +27,8 @@ function updateDocInOrdered(state, action) {
2727
state[storeUnderKey] || [],
2828
action.meta.doc,
2929
item =>
30-
updateObject(
30+
Object.assign(
31+
{},
3132
item,
3233
subcollection
3334
? { [subcollection.collection]: action.payload.ordered }
@@ -78,6 +79,7 @@ export default function orderedReducer(state = {}, action) {
7879
return pick(state, action.preserve); // pick returns a new object
7980
}
8081
return state;
82+
// TODO: LISTENER_ERROR that sets null in a way that is configurable (v0.3.0)
8183
default:
8284
return state;
8385
}

src/utils/reducers.js

+35-11
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { isFunction, isBoolean, isArray, pick } from 'lodash';
2+
13
/**
24
* Create a path array from path string
35
* @param {String} path - Path seperated with slashes
@@ -59,6 +61,7 @@ export const combineReducers = reducers => (state = {}, action) =>
5961
* action associates with (used for storing data under a path different
6062
* from its collection/document)
6163
* @return {String} String path to be used within reducer
64+
* @private
6265
*/
6366
export function pathFromMeta(meta) {
6467
if (!meta) {
@@ -82,24 +85,14 @@ export function pathFromMeta(meta) {
8285
return basePath.concat(`.${mappedCollections.join('.')}`);
8386
}
8487

85-
/**
86-
* Encapsulate the idea of passing a new object as the first parameter
87-
* to Object.assign to ensure we correctly copy data instead of mutating
88-
* @param {Object} oldObject - Object before update
89-
* @param {Object} newValues - New values to add to the object
90-
* @return {Object} Object with new values
91-
*/
92-
export function updateObject(oldObject, newValues) {
93-
return Object.assign({}, oldObject, newValues);
94-
}
95-
9688
/**
9789
* Update a single item within an array
9890
* @param {Array} array - Array within which to update item
9991
* @param {String} itemId - Id of item to update
10092
* @param {Function} updateItemCallback - Callback dictacting how the item
10193
* is updated
10294
* @return {Array} Array with item updated
95+
* @private
10396
*/
10497
export function updateItemInArray(array, itemId, updateItemCallback) {
10598
const updatedItems = array.map(item => {
@@ -115,3 +108,34 @@ export function updateItemInArray(array, itemId, updateItemCallback) {
115108

116109
return updatedItems;
117110
}
111+
112+
/**
113+
* Preserve slice of state based on preserve settings for that slice. Settings
114+
* for support can be any of type `Boolean`, `Function`, or `Array`.
115+
* @param {Object} state - slice of redux state to be preserved
116+
* @param {Boolean|Function|Array} preserveSetting [description]
117+
* @param {Object} nextState - What state would have been set to if preserve
118+
* was not occuring.
119+
* @return {Object} Slice of state with values preserved
120+
* @private
121+
*/
122+
export function preserveValuesFromState(state, preserveSetting, nextState) {
123+
// Return original state if preserve is true
124+
if (isBoolean(preserveSetting) && preserveSetting) {
125+
return nextState ? { ...state, ...nextState } : state;
126+
}
127+
128+
// Return result of function if preserve is a function
129+
if (isFunction(preserveSetting)) {
130+
return preserveSetting(state, nextState);
131+
}
132+
133+
// Return keys listed within array
134+
if (isArray(preserveSetting)) {
135+
return pick(state, preserveSetting); // pick returns a new object
136+
}
137+
138+
throw new Error(
139+
'Invalid preserve parameter. It must be an Object or an Array',
140+
);
141+
}

0 commit comments

Comments
 (0)