feat(reducers): support reference data type
hello
Do you want to request a feature or report a bug?
feature
What is the current behavior? data type
db.collection("posts").doc("QyJJKtQhNA2QeHFVqfCR") = {
content: String,
author: Reference // 'users/jRjjlMyTBENjvln3pQpGwKgdvAa2'
}
query
{ collection: 'posts', doc: postId }
result
{ content: '', author: DocumentReference }
A DocumentReference refers to a document location in a Firestore database and can be used to write, read, or listen to the location. The document at the referenced location may or may not exist. A DocumentReference can also be used to create a CollectionReference to a subcollection.

What is the expected behavior?
{ content: '', author: { displayName: '', avatarUrl: '' } }
I am aware that it's not a good idea, but I'm using it with react-redux-firebase as shown below, as a temporary solution.
import { compose, withProps, renderComponent } from "recompose";
import { connect } from "react-redux";
import { firestoreConnect, getVal, isLoaded } from "react-redux-firebase";
const spinnerWhileLoading = isLoading =>
branch(
isLoading,
renderComponent(() => <div>spinner</div>)
)
export default compose(
firestoreConnect(({ postId }) => [{ collection: "posts", doc: postId }]),
connect((state, { postId }) => ({
post: getVal(state.firestore, `data/posts/${postId}`)
})),
spinnerWhileLoading(props => !isLoaded(props.post)),
firestoreConnect(({ post }) => [
{ collection: "users", doc: post.author.id }
]),
connect((state, { post }) => ({
author: getVal(state.firestore, `data/users/${post.author.id}`)
})),
spinnerWhileLoading(props => !isLoaded(props.author)),
withProps(({ post, author, ...props }) => ({
post: { ...post, id: props.post, author }
}))
)(PostComponent);
@jeongsd The solution you provided is actually a great way to do it and is currently what I have done too.
redux-firestore hasn't handled reference objects for a few reasons:
- Everything stored in redux state is currently only plain JS objects/arrays (i.e. not Classes or Objects with extra methods)
- Storing strings with keys will work with
populatefromreact-redux-firebase- though with a change like this might mean one could leave off therootparameter when callingpopulateon an object with document references (which sounds cool)
I am open to ideas for how things should change though
I think the expected behavior from jeongsd would be great.
Just trying to implement the case that I have subcollection with references to another collection. Is there currently some easy method to call/populate that?
@jollyBaker Not yet, but definitely high on the priority list.
Hello!
Is there a workaround that would allow using populates with DocumentReference instead of just plain-string ids?
I would suggest allowing user to pass a function that would extract the document id from firestore attribute in case that it is not a plain-string but some complex structure. This would also cover the case of DocumentReference.
Documentation for Populate has an example data which is using plain string IDs.
However, it would be helpful to consider the case when Firestore's built-in Reference data type is used for "linking", for example:
"todos": {
"ASDF123": {
"text": "Some Todo Item",
"owner": DocumentReference("users/Iq5b0qK2NtgggT6U3bU6iZRGyma2")
}
},
"users": {
"Iq5b0qK2NtgggT6U3bU6iZRGyma2": {
"displayName": "Morty Smith",
"email": "[email protected]"
}
}
In this case "owner" attribute always refers to the documents in "users" collection, so one could use plain string ids instead of references. However, as I am not allowed to change the data schema, I have to seek for a work around.
Once I have realized that populates parameter could be a function I have created a workaround:
const collection = 'todos';
const populates = (dataKey, originalData) => {
if (originalData && originalData['owner'] && !originalData['ownerData']) {
originalData['ownerData'] = originalData.owner.path.split('/')[1];
}
return [{child: 'ownerData', root: 'users'}]
};
const mapStateToProps = state => {
return {
uid: state.firebase.auth.uid,
todos: populate(state.firestore, collection, populates)
}
};
const enhancer = compose(
firestoreConnect([{collection, populates}]),
connect(mapStateToProps),
);
This code first extracts the user document ID from the owner attribute and then uses this ID to initialize a new ownerData attribute. Finally ownerData attribute is populated as usual.
Once I have realized that
populatesparameter could be a function I have created a workaround
Oh my, this helped so much! This isn't documented anywhere? I have some collection with documents whose IDs repeat userIDs in user collection and I wanted to populate these docs with user profiles by these IDs. This is the only way I found, am I right?
@nt4f04uNd Not sure it is documented, but if it is, not clearly!