@Salakar I'm attempting to use the pattern you referenced in a previous issue.
The Problem
In my iOS app, it's possible to have multiple instances of a component mounted at the same time.
Think Instagram โ when a user taps a photo in Instagram and then taps the photo owner's avatar, the user scene pushes in from the right.
If she then goes on to tap the same photo in the user's list of photos, a clone of the first scene will also push in the from right.
In my app, all scenes need to mount, establish the Firebase listener, get a snapshot of the initial data and listen for changes after that.
Everything works great for the first component mounted. In any subsequent components, however, the listeners don't immediately trigger the callback.
I suppose makes sense considering the docs:
Firebase data is retrieved by attaching an asynchronous listener to a firebase.database.Reference. The listener is triggered once for the initial state of the data and again anytime the data changes.
It's as though Firebase sees each subsequent listener as the same listener and thus doesn't trigger.
Data Structure
In my code, I'm following Firebase's recommended data fan-out approach which seeks to create "data that scales."
I've included the following JSON, so the complexity of the code that follows will be more clear.
{
"displayName" : "Andrew Henderson",
"host" : {
"-KgC-c0WvT0P1R-aDmVo" : true,
"-KgC0D94Cxs05GX8qWBc" : true,
"-KgC0s7f9lwweeDm3oeg" : true,
"-KgC0ypAODARXxvbva5F" : true
},
"puid" : "-Kg6pefxyFtyfPVe-aIo",
"timestamp" : 1490485162946,
"uid" : "H63dvD6TvJRZnpPwf9o0FejqmEf2"
}
Code
In my component, I set up a reference to a user's posts which returns the host
object.
I then create a reference to all of the individual posts using those keys and receive the values for the full posts.
constructor(props) {
super(props);
const { uid } = props;
// Set firebase refs
this.postsRef = firebase.database().ref('posts');
this.hostsRef = firebase.database()
.ref(`users/${uid}/host`)
.orderByChild('startTime');
this.hostedPostRefs = {};
// Keep a raw copy of this user's posts
this.hostedPosts = {};
// ListView DataSource instance
this.hostedPostsDataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1._key !== r2._key,
});
// Initial State
this.state = {
loading: true,
hostedPostsDataSource: this.hostedPostsDataSource.cloneWithRows(this.hostedPosts),
};
}
componentDidMount() {
this.hostsRef.on('value', this._onHostsReceived)
}
componentWillUnmount() {
this.hostsRef.off();
forEach(this.hostedPostRefs, hostedPostRef => {
hostedPostRef.off()
})
}
_onHostsReceived = (snapshot) => {
snapshot.forEach(this._requestPost)
};
_requestPost = (snapshot) => {
const { key } = snapshot;
if (!this.hostedPostRefs[key]) {
this.hostedPostRefs[key] = this.postsRef.child(key);
this.hostedPostRefs[key].on('child_changed event', this._onPostReceived)
}
};
_onPostReceived = (snapshot) => {
this.hostedPosts[snapshot.key] = snapshot.val();
this.setState({
hostedPostsDataSource: this.hostedPostsDataSource.cloneWithRows(this.hostedPosts)
});
};