Skip to content

Latest commit

 

History

History
359 lines (297 loc) · 8.95 KB

populate.md

File metadata and controls

359 lines (297 loc) · 8.95 KB

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.

Initial data from populate is placed into redux in a normalized pattern following defined redux practice of normalizing. populate helper used within the connect function then builds populated data out of normalized data within redux (NOTE: This does not apply if you are using v1.1.5 or earlier).

A basic implementation can look like so:

import { compose } from 'redux'
import { connect } from 'react-redux'
import { firebaseConnect, populate } from 'react-redux-firebase'

const populates = [
  { child: 'owner', root: 'users' } // replace owner with user object
]

const enhance = compose(
  firebaseConnect([
    // passing populates parameter also creates all necessary child queries
    { path: 'todos', populates }
  ]),
  connect(({ firebase }) => ({
    // populate original from data within separate paths redux
    todos: populate(firebase, 'todos', populates),
    // firebase.ordered.todos or firebase.data.todos for unpopulated todos
  }))
)

export default enhance(SomeComponent)

Some Things To Note

  • Population happens in two parts:
    1. firebaseConnect - based on populate settings, queries are created for associated keys to be replaced. Query results are stored in redux under the value of root in the populate settings.
    2. connect - Combine original data at path with all populate data (in redux from queries created by passing populate settings to firebaseConnect)
  • Populate creates a query for each key that is being "populated", but does not create multiple queries for the same key
  • Results of populate queries are placed under their root

Examples

List of todo items where todo item can contain an owner parameter which is a user's UID like so:

{ "text": "Some Todo Item", "owner": "Iq5b0qK2NtgggT6U3bU6iZRGyma2" }

Populate allows you to replace the owner parameter with another value on Firebase under that key. That value can be a string (number and boolean treated as string), or an object

Initial data from populate is placed into redux in a normalized pattern following defined redux practice of normalizing. populatedDataToJS helper used in the connect function then builds populated data out of normalized data within redux (NOTE: This does not apply if you are using v1.1.5 or earlier).

Example Data
"todos": {
  "ASDF123": {
    "text": "Some Todo Item",
    "owner": "Iq5b0qK2NtgggT6U3bU6iZRGyma2"
   }
 },
 "displayNames": {
   "Iq5b0qK2NtgggT6U3bU6iZRGyma2": "Morty Smith",
   "6Ra53mf3U9Qmdwah6rXBMgY8smu1": "Rick Sanchez"
 }
 "users": {
   "Iq5b0qK2NtgggT6U3bU6iZRGyma2": {
     "displayName": "Morty Smith",
     "email": "[email protected]"
   },
   "6Ra53mf3U9Qmdwah6rXBMgY8smu1": {
     "displayName": "Rick Sanchez",
     "email": "[email protected]"
   }
 }

String, Number, or Boolean

When trying to replace the owner parameter with a string such as a displayName from a /displayNames root follow the pattern of #populate=paramToPopulate:populateRoot.

Example Query
const populates = [
  { child: 'owner', root: 'displayNames' }
]
const enhance = compose(
  firebaseConnect([
    { path: '/todos', populates }
    // '/todos#populate=owner:displayNames', // equivalent string notation
  ]),
  connect(
    ({ firebase }) => ({
      todos: populate(firebase, 'todos', populates),
    })
  )
)
Result
ASDF123: {
  text: 'Some Todo Item',
  owner: 'Morty Smith'
 }

Object

Population can also be used to populate a parameter with an object. An example of this would be populating the owner parameter, which is an ID, with the matching key from the users list.

Example Query
const populates = [
  { child: 'owner', root: 'users' }
]
const enhance = compose(
  firebaseConnect([
    { path: '/todos', populates }
    // '/todos#populate=owner:users' // equivalent string notation
  ]),
  connect(
    ({ firebase }) => ({
      todos: populate(firebase, 'todos', populates),
    })
  )
)
Example Result
ASDF123: {
  text: 'Some Todo Item',
  owner: {
    displayName: 'Morty Smith',
    email: '[email protected]'
  }
}
Keep Object's Key {#keyProp}

Often when populating, you will want to keep the key that was originally there (before being replaced by populated value). This is supported through the use of keyProp:

NOTE: Similar results also be accomplished using childAlias since child (key in this case) will be preserved if populate result is "aliased"

Example Query
const populates = [
  { child: 'owner', root: 'users', keyProp: 'key' }
]

const enhance = compose(
  firebaseConnect([
    { path: '/todos', populates }
  ]),
  connect(
    ({ firebase }) => ({
      todos: populate(firebase, 'todos', populates),
    })
  )
)
Example Result
ASDF123: {
  text: 'Some Todo Item',
  owner: {
    key: 'Iq5b0qK2NtgggT6U3bU6iZRGyma2',
    displayName: 'Scott Prue',
    email: '[email protected]'
  }
}

Place Result On Another Parameter {#childAlias}

There is also the option to place the results of a populate on another parameter instead of replacing the original child (i.e. "alias" the child result). An example of this could be populating the owner parameter onto the ownerObj parameter, which would leave the owner parameter intact (since the child from the populate was "aliased" to ownerObj).

For more details including the initial feature request, checkout issue #126.

Example
const populates = [
  { child: 'owner', root: 'users', childAlias: 'ownerObj' }
]

const enhance = compose(
  firebaseConnect([
   { path: '/todos', populates }
  ]),
  connect(
    ({ firebase }) => ({
      todos: populate(firebase, 'todos', populates),
    })
  )
)
Example Result
ASDF123: {
  text: 'Some Todo Item',
  owner: "Iq5b0qK2NtgggT6U3bU6iZRGyma2",
  ownerObj: {
    displayName: "Morty Smith",
    email: '[email protected]',
  }
}

Object's Parameter {#childParam}

There is also the option to load a parameter from within a population object. An example of this could be populating the owner parameter with the email property of the user with a matching ID:

Example
const populates = [
  { child: 'owner', root: 'users', childParam: 'email' }
]

const enhance = compose(
  firebaseConnect([
   { path: '/todos', populates }
   // '/todos#populate=owner:users:email' // equivalent string notation
  ]),
  connect(
    ({ firebase }) => ({
      todos: populate(firebase, 'todos', populates),
    })
  )
)
Example Result
ASDF123: {
  text: 'Some Todo Item',
  owner: '[email protected]'
}

Profile Parameters

To Populate parameters within profile/user object, include the profileParamsToPopulate parameter within your react-redux-firebase config.

Parameter

Example Config

Populating username with username from usernames ref.

const config = {
  userProfile: 'users',
  profileParamsToPopulate: [
    { child: 'displayName', root: 'displayNames' } // object notation
  ]
}
Initial Data
{
  users: {
    $uid: {
      email: '[email protected]',
      displayName: 'Iq5b0qK2NtgggT6U3bU6iZRGyma2'
    }
  }
}
Example Result
{
  users: {
    $uid: {
      email: '[email protected]',
      displayName: 'someuser'
    }
  }
}

List of Items

Example Config
const config = {
  userProfile: 'users',
  profileParamsToPopulate: [ 'todos:todos' ] // populate list of todos from todos ref
}
Initial Data
{
  users: {
    $uid: {
      email: '[email protected]',
      todos: {
        0: "ASDF123"
      }
    }
  }
}
Example Result
{
  users: {
    $uid: {
      email: '[email protected]',
      todos: {
        ASDF123: {
          text: 'Some Todo Item',
          owner: "Iq5b0qK2NtgggT6U3bU6iZRGyma2"
        }
      }
    }
  },
  todos: {
    ASDF123: {
      text: 'Some Todo Item',
      owner: "Iq5b0qK2NtgggT6U3bU6iZRGyma2"
    }
  }
}

Use with Firestore

To use populate with Firestore, just replace firebaseConnect with firestoreConnect with the corresponding charaters.

import { firestoreConnect, populate } from 'react-redux-firebase';

const populates = [
  { child: 'owner', root: 'users', childAlias: 'ownerObj' }
]

const enhance = compose(
  firestoreConnect([
    { collection: 'todos', populates }
  ]),
  connect(
    ({ firebase }) => ({
      todos: populate(firebase, 'todos', populates),
    })
  )
)