Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion docs/queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,13 @@ queryParams: [`limitToFirst=${limitToFirst}`, `startAt=${startAt}`, 'orderByValu
queryParams: ['orderByValue', `limitToFirst=${limitToFirst}`, `startAt=${startAt}`],
```

If you would like to prevent parsing yourself (i.e. keep limit values as strings), you can pass [`notParsed`](#notParsed) as a queryParam:
If you would like to prevent or cause parsing of query params yourself, you can pass [`notParsed`](#notParsed) or [`parsed`](#parsed) as a queryParam:

```js
// limitToFirst and startAt remain as strings and are NOT automatically parsed
queryParams: ['notParsed', `limitToFirst=${limitToFirst}`, `startAt=${startAt}`, 'orderByValue'],
// limitToFirst and startAt are parsed into numbers if possible
queryParams: ['parsed', `limitToFirst=${limitToFirst}`, `startAt=${startAt}`, 'orderByValue'],
```

More on [`notParsed` below](#notParsed)
Expand Down Expand Up @@ -344,6 +346,7 @@ firebaseConnect([
])
```


## notParsed {#notParsed}

Can be used to keep internal parsing from happening. Useful when attempting to search a number string using `equalTo`
Expand All @@ -363,6 +366,27 @@ firebaseConnect([
])
```

## parsed {#parsed}

Internally parse following query params. Useful when attempting to parse

**NOTE**: `orderByChild`, `orderByPriority`, and `orderByValue` will cause this to be enabled by default. Parsing will remain enabled for the rest of the query params until `notParsed` is called.

#### Examples
1. Order by child parameter equal to a number string. Equivalent of searching for `'123'` (where as not using `notParsed` would search for children equal to `123`)
```js
firebaseConnect([
{
path: '/todos',
queryParams: [
'parsed', // causes automatic parsing
'equalTo=123' // 123 is treated as a number instead of a string
'orderByChild=createdBy',
]
}
])
```

## storeAs {#populate}

By default the results of queries are stored in redux under the path of the query. If you would like to change where the query results are stored in redux, use `storeAs`:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-redux-firebase",
"version": "2.0.0-beta.16",
"version": "2.0.0-beta.17",
"description": "Redux integration for Firebase. Comes with a Higher Order Components for use with React.",
"main": "lib/index.js",
"module": "es/index.js",
Expand Down
13 changes: 10 additions & 3 deletions src/actions/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,11 @@ const handleAuthStateChange = (dispatch, firebase, authData) => {

watchUserProfile(dispatch, firebase)

dispatch({ type: actionTypes.LOGIN, auth: authData })
dispatch({
type: actionTypes.LOGIN,
auth: authData,
preserve: config.preserveOnLogin
})

// Run onAuthStateChanged if it exists in config
if (isFunction(config.onAuthStateChanged)) {
Expand All @@ -349,7 +353,11 @@ export const handleRedirectResult = (dispatch, firebase, authData) => {
firebase._.authUid = user.uid // eslint-disable-line no-param-reassign
watchUserProfile(dispatch, firebase)

dispatch({ type: actionTypes.LOGIN, auth: user })
dispatch({
type: actionTypes.LOGIN,
auth: user,
preserve: firebase._.config.preserveOnLogin
})

createUserProfile(dispatch, firebase, user, {
email: user.email,
Expand Down Expand Up @@ -422,7 +430,6 @@ export const init = (dispatch, firebase) => {
export const login = (dispatch, firebase, credentials) => {
if (firebase._.config.resetBeforeLogin) {
dispatchLoginError(dispatch, null)
dispatch({ type: actionTypes.UNLOAD_PROFILE })
}

const { method, params } = getLoginMethodAndParams(firebase, credentials)
Expand Down
1 change: 0 additions & 1 deletion src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ export const actionTypes = {
REMOVE: `${actionsPrefix}/REMOVE`,
MERGE: `${actionsPrefix}/MERGE`,
SET_PROFILE: `${actionsPrefix}/SET_PROFILE`,
UNLOAD_PROFILE: `${actionsPrefix}/UNLOAD_PROFILE`,
LOGIN: `${actionsPrefix}/LOGIN`,
LOGOUT: `${actionsPrefix}/LOGOUT`,
LOGIN_ERROR: `${actionsPrefix}/LOGIN_ERROR`,
Expand Down
17 changes: 11 additions & 6 deletions src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,14 +283,11 @@ export const populate = (state, path, populates, notSetValue) => {
const dataHasPopulateChilds = some(populatesForData, p =>
has(data, p.child)
)

// Single object that contains at least one child parameter
if (dataHasPopulateChilds) {
// Data is a single object, resolve populates directly
const populatedValue = populatesForData
return populatesForData
.map(p => populateChild(state, data, p))
.reduce((acc, v) => defaultsDeep(v, acc), data)

return populatedValue
}
} else {
// When using a path in ordered, data will be an array instead of an object
Expand All @@ -299,7 +296,7 @@ export const populate = (state, path, populates, notSetValue) => {
some(array, item => has(item, key))

// Check to see if child exists for every populate
const dataHasPopulateChilds = every(populatesForData, populate =>
const dataHasPopulateChilds = some(populatesForData, populate =>
someArrayItemHasKey(data)(['value', populate.child])
)

Expand Down Expand Up @@ -335,6 +332,14 @@ export const populate = (state, path, populates, notSetValue) => {
? populates(key, child)
: populates
)
// check each data child for each populate parameter
const dataHasPopulateChilds = some(populatesForDataItem, p =>
has(child, p.child)
)
// Return unmodified child if populate param does not exist
if (!dataHasPopulateChilds) {
return child
}
// combine data from all populates to one object starting with original data
return reduce(
map(populatesForDataItem, p => populateChild(state, child, p)),
Expand Down
23 changes: 12 additions & 11 deletions src/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ const {
START,
SET,
SET_PROFILE,
UNLOAD_PROFILE,
MERGE,
LOGIN,
LOGOUT,
Expand Down Expand Up @@ -250,7 +249,11 @@ export const authReducer = (state = { isLoaded: false, isEmpty: true }, action)
}
}
const auth = action.auth.toJSON ? action.auth.toJSON() : action.auth
return { ...auth, isEmpty: false, isLoaded: true }
// Support keeping data
if (action.preserve && action.preserve.auth) {
return pick({ ...state, ...auth }, action.preserve.auth) // pick returns a new object
}
return { ...state, ...auth, isEmpty: false, isLoaded: true }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

before we were returning { ...auth, isEmpty: false, isLoaded: true } and now we are returning { ...state, ...auth, isEmpty: false, isLoaded: true }. Any reason we added ...state?

Copy link
Owner Author

@prescottprue prescottprue Nov 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question! The goal was to preserve anything in state that was there, as well as spread the new parameters onto it. I think that this doesn't really offer that much advantage, and could be unclear to some, so I am going to change it back.

I think it still makes sense to pass { ...state, ...auth } to pick though since some want to be able to preserve isLoaded state and potentially other things that were already in state. What are your thoughts?

case AUTH_LINK_SUCCESS:
if (!action.payload) {
return {
Expand All @@ -268,10 +271,7 @@ export const authReducer = (state = { isLoaded: false, isEmpty: true }, action)
case LOGOUT:
// Support keeping data when logging out
if (action.preserve && action.preserve.auth) {
return pick(
{ ...state, isLoaded: true, isEmpty: true },
action.preserve.auth
) // pick returns a new object
return pick(state, action.preserve.auth) // pick returns a new object
}
return { isLoaded: true, isEmpty: true }
default:
Expand Down Expand Up @@ -323,7 +323,11 @@ export const profileReducer = (state = { isLoaded: false, isEmpty: true }, actio
isEmpty: false,
isLoaded: true
}
case UNLOAD_PROFILE:
case LOGIN:
// Support keeping data when logging out
if (action.preserve && action.preserve.profile) {
return pick(state, action.preserve.profile) // pick returns a new object
}
return {
isEmpty: true,
isLoaded: false
Expand All @@ -332,10 +336,7 @@ export const profileReducer = (state = { isLoaded: false, isEmpty: true }, actio
case AUTH_EMPTY_CHANGE:
// Support keeping data when logging out
if (action.preserve && action.preserve.profile) {
return pick(
{ ...state, isLoaded: true, isEmpty: true },
action.preserve.profile
) // pick returns a new object
return pick(state, action.preserve.profile) // pick returns a new object
}
return { isLoaded: true, isEmpty: true }
default:
Expand Down
4 changes: 4 additions & 0 deletions src/utils/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ export const applyParamsToQuery = (queryParams, query) => {
// support disabling internal number parsing (number strings)
doNotParse = true
break
case 'parsed':
// support disabling internal number parsing (number strings)
doNotParse = false
break
case 'equalTo':
let equalToParam = !doNotParse ? tryParseToNumber(param[1]) : param[1]
equalToParam = equalToParam === 'null' ? null : equalToParam
Expand Down
107 changes: 107 additions & 0 deletions tests/mockData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
export const exampleData = {
data: {
some: 'data',
projects: {
CDF: {
owner: 'ABC',
category: 'cat1',
notes: {
123: true
},
collaborators: {
ABC: true,
abc: true
}
},
GHI: {
owner: 'ABC',
category: 'cat2'
},
OKF: {
owner: 'asdfasdf',
notes: {
123: true
},
collaborators: {
ABC: true,
abc: true
}
},
QRS: {
owner: 'ABC',
category: 'cat1',
nested: {
owner: 'ABC'
},
notes: {
123: true
},
collaborators: {
ABC: true,
abc: true
}
},
TUV: {
owner: 'ABC',
notes: {
123: true
},
collaborators: {
ABC: true,
abc: true
}
}
},
users: {
ABC: {
displayName: 'scott'
}
},
categories: {
cat1: {
displayName: 'magic'
},
cat2: {
displayName: 'animals'
}
},
notes: {
123: {
text: 'Some Text'
}
},
missing: {
data: null
},
roles: {
tester: {
somePermission: true
}
}
},
ordered: {
projects: [
{
value: {
owner: 'ABC',
notes: {
123: true
},
collaborators: {
ABC: true,
abc: true
}
},
key: 'JKF'
}
]
},
timestamp: { 'some/path': { test: 'key' } },
snapshot: { some: 'snapshot' },
profile: {
role: 'tester',
notes: {
123: true
}
}
}
Loading