AndrewHenderson
AndrewHenderson

Reputation: 4972

Firebase on value event only fires once

In my React Native 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.

I need all instances of the scene 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 instance.

However, in any subsequent instances, the listeners are defined, but their callbacks don't immediately execute, so the component does not receive a snapshot.

Worth noting is that both listeners do execute their callbacks when the data changes.

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 the hosts
  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)
  });
};

Here's what the Firebase docs have to say:

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.

I'm using React Native Firebase. Given that, I'm not sure if this is a bug in React Native Firebase or Firebase's API behaving as intended.

Upvotes: 2

Views: 913

Answers (1)

AndrewHenderson
AndrewHenderson

Reputation: 4972

It was explained to me by a React Native Firebase contributor that there is a mistake in the library's db implementation and that it will be resolved in the near future.

Upvotes: 1

Related Questions