Queries
Creating
Firebase Real Time Database queries can be created in two ways:
- Manually - Using
watchEvents
orwatchEvent
(requires managing of listeners) - Automatically - Using
firebaseConnect
HOC (manages mounting/unmounting)
Manually
Queries can be created manually by using watchEvent
or watchEvents
. This is useful to load data on some event such as a button click.
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { compose } from 'redux'
import { withFirebase, isLoaded, isEmpty } from 'react-redux-firebase'
const Todos = ({ firebase }) => {
// Build Todos list if todos exist and are loaded
const todosList = !isLoaded(todos)
? 'Loading'
: isEmpty(todos)
? 'Todo list is empty'
: Object.keys(todos).map(
(key, id) => <TodoItem key={key} id={id} todo={todos[key]}/>
)
return (
<div>
<h1>Todos</h1>
<ul>
{todosList}
</ul>
<button onClick={() => firebase.watchEvent('value', 'todos')}>
Load Todos
</button>
</div>
)
}
export default compose(
withFirebase, // or firebaseConnect()
connect(
(state) => ({
todos: state.firebase.data.todos,
// profile: state.firebase.profile // load profile
})
)
)(Todos)
Though doing things manually is great to understand what is going on, it comes with the need to manage these listeners yourself.
Fun Fact - firebaseConnect
actually calls watchEvents
internally on component mount/unmount and when props change.
Automatically
firebaseConnect
accepts an array of paths for which to create queries. When listening to paths, it is possible to modify the query with any of Firebase's included query methods.
The results of the queries created by firebaseConnect
are written into redux state under the path of the query for both state.firebase.ordered
and state.firebase.data
.
NOTE:
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
(more below).
Ordered vs Data (by key)
data
In order to get data from state by key, use data
.
Examples
- Get an object of projects by key
compose(
firebaseConnect(props => [
{ path: 'projects' }
]),
connect((state, props) => ({
projects: state.firebase.data.projects,
}))
)
ordered
In order to get ordered data, use orderedToJS
Examples
- Get list of projects ordered by key
compose(
firebaseConnect(props => [
{ path: 'projects', queryParams: ['orderByKey'] }
]),
connect((state, props) => ({
projects: state.firebase.ordered.projects,
}))
)
Populate
Populate allows you to replace IDs within your data with other data from Firebase. This is very useful when trying to keep your data flat. Some would call it a join, but it was modeled after the mongo populate method.
Visit Populate Section for full documentation.
Types of Queries
There are multiple types of queries
once
To load a firebase location once instead of binding, the once option can be used:
Internally Uses Firebase Method: orderByPriority
firebaseConnect([
{ type: 'once', path: '/todos' }
])
Query Params
Note: orderByChild
, orderByValue
, and orderByPriority
enable automatic parsing of query params that follow them for convince. This means that the order of query params can affect which query is created. For example:
// Works since limitToFirst and startAt are parsed into numbers
queryParams: [`limitToFirst=${limitToFirst}`, `startAt=${startAt}`, 'orderByValue'],
// COULD CAUSE UNEXPECTED BEHAVIOR!!! Values passed to limitToFirst and startAt will remain as strings (i.e not parsed)
queryParams: ['orderByValue', `limitToFirst=${limitToFirst}`, `startAt=${startAt}`],
If you would like to prevent or cause parsing of query params yourself, you can pass notParsed
or parsed
as a queryParam:
// 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
orderByChild
To order the query by a child within each object, use orderByChild.
Internally Uses Firebase Method: orderByChild
Example
Ordering a list of todos by the text parameter of the todo item (placing them in alphabetical order).
firebaseConnect([
{ path: '/todos', queryParams: [ 'orderByChild=text' ]}
'/todos#orderByChild=text' // string notation
])
orderByKey
Order a list by the key of each item. Since push keys contain time, this is also a way of ordering by when items were created.
Internally Uses Firebase Method:
orderByKey
Example
Ordering a list of todos based on their key (puts them in order of when they were created)
firebaseConnect([
{ path: '/todos', queryParams: [ 'orderByKey' ]}
// '/todos#orderByKey' // string notation
])
orderByValue
Order a list by the value of each object. Internally runs
Internally Uses Firebase Method: orderByValue
Example
Ordering a list of score's based on score's value
firebaseConnect([
{ path: '/scores', queryParams: [ 'orderByValue' ] }
// `scores#orderByValue` // string notation
])
orderByPriority
Order a list by the priority of each item.
Internally Uses Firebase Method: orderByPriority
Example
Ordering a list based on priorities
firebaseConnect([
{ path: '/scores', queryParams: [ 'orderByPriority' ] }
// `scores#orderByPriority` // string notation
])
limitToFirst
Limit query results to the first n number of results.
Internally Uses Firebase Method: limitToFirst
Examples
Displaying only the first todo item
firebaseConnect([ { path: '/todos', queryParams: [ 'limitToFirst=1' ] } // '/todos#limitToFirst' // string notation ])
Displaying only the first 10 todo items
firebaseConnect([ { path: '/todos', queryParams: [ 'orderByChild=createdBy', 'equalTo=123' ] } // '/todos#limitToFirst=10' // string notation ])
limitToLast
Limit query results to the last n number of results
Internally Uses Firebase Method: limitToLast
Examples
Only the last todo item
firebaseConnect([ { path: '/todos', queryParams: [ 'limitToLast' ] } // '/todos#limitToLast' // string notation ])
Only the last 10 todo items
firebaseConnect([ { path: '/todos', queryParams: [ 'limitToLast=10' ] } // '/todos#limitToLast=10' // string notation ])
startAt
Start query at a specific location by providing the specific number or value
Internally Uses Firebase Method: startAt
Examples
- Starting at the fifth item
firebaseConnect([ { path: '/todos', queryParams: [ 'startAt=5', 'limitToFirst=2' ] } // 'todos#startAt=5&limitToFirst=2' // string notation ])
- Paginate results
firebaseConnect([ { path: '/todos', queryParams: [ 'startAt=5', 'limitToFirst=10' ] } // 'todos#startAt=5&limitToFirst=10' // string notation ])
- Non-number values
firebaseConnect([ { path: '/todos', queryParams: [ 'startAt=5', 'limitToFirst=10' ] } // 'todos#startAt=val1&limitToFirst=10' // string notation ])(SomeComponent)
endAt
End query at a specific location by providing the specific number or value
Internally Uses Firebase Method: endAt
Examples
- Usage with startAt
firebaseConnect([ { path: '/todos', queryParams: [ 'orderByChild=added', 'startAt=1', 'endAt=5' ] } // 'todos#orderByChild=added&startAt=1&endAt=5' // string notation ])
equalTo
Limit query results with parameter equal to previous query method (i.e when used with orderByChild, it limits results with child equal to provided value).
Internally Uses Firebase Method: equalTo
Parsing
The following are internally parsed:
null
boolean
number
This means the actual value will be parsed instead of the string containing the value. If you do not want this to happen, look at the notParsed
query parameter below.
Examples
- Order by child parameter
firebaseConnect([ { path: '/todos', queryParams: [ 'orderByChild=createdBy', 'equalTo=ASD123' ] } // 'todos#orderByChild=createdBy&equalTo=ASD123', // string notation ])
notParsed
Can be used to keep internal parsing from happening. Useful when attempting to search a number string using equalTo
Examples
- Order by child parameter equal to a number string. Equivalent of searching for
'123'
(where as not usingnotParsed
would search for children equal to123
)firebaseConnect([ { path: '/todos', queryParams: [ 'orderByChild=createdBy', 'notParsed', // keeps equalTo from automatically parsing 'equalTo=123' ] } ])
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.
Added as part of v2.0.0-beta.17
Examples
Order by child parameter equal to a number string. Equivalent of searching for
'123'
(where as not usingnotParsed
would search for children equal to123
)firebaseConnect([ { path: '/todos', queryParams: [ 'parsed', // causes automatic parsing 'equalTo=123' // 123 is treated as a number instead of a string 'orderByChild=createdBy', ] } ])
storeAs
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
.
Examples
- Querying the same path with different query parameters
const myProjectsReduxName = 'myProjects'
compose(
firebaseConnect(props => [
{ path: 'projects' },
{
path: 'projects',
storeAs: myProjectsReduxName,
queryParams: ['orderByChild=uid', '123']
}
]),
connect((state, props) => ({
projects: state.firebase.data.projects,
myProjects: state.firebase.data[myProjectsReduxName], // use storeAs path to gather from redux
}))
)
Why?
Data is stored in redux under the path of the query for convince. This means that two different queries to the same path (i.e. todos
) will both place data into state.data.todos
even if their query parameters are different.
Populate
Populate allows you to replace IDs within your data with other data from Firebase. This is very useful when trying to keep your data flat. Some would call it a join, but it was modeled after the mongo populate method.
Visit Populate Section for full documentation.