0

Using: Vanilla React and Flux Firebase

I'm trying to keep my store synced with realtime updates and so I've been following the instructions of this article Handling Synchronized Arrays with Real-Time Firebase Data

I've placed a call to a the WebAPI utilities class to fetch the initial data and placed it in the componentWillMount listener of my top level component as instructed by many.The code looks like so.

var MessageSection = require('./MessageSection.react');
var React = require('react');
var ThreadSection = require('./ThreadSection.react');
var ChatWebAPIUtils = require('../utils/ChatWebAPIUtils');
var ChatApp = React.createClass({

  componentWillMount: function(){
    ChatWebAPIUtils.getAllMessages(); **Here is my call to an action**
  }

  render: function() {
    return (
      <div className="chatapp">
        <ThreadSection />
        <MessageSection />
      </div>
    );
  }

});

module.exports = ChatApp;

The problem is that even though my store will be loaded correctly, it looks like because it's an asynchronous fetch there will be calls to render components whose data is still not ready.

What is the conventional way to wait for the store be initialized before my program tries to render itself?

Nick Pineda
  • 6,354
  • 11
  • 46
  • 66
  • How do your components access the data? Do they utilize some functions like ChatWebAPIUtils.messages? – Patrick Hübl-Neschkudla Sep 10 '15 at 21:13
  • the utils method calls a Firebase listener " .once(value) " which executes once to grab the data from Firebase and in it's callback an Action is called to load the data into the store. – Nick Pineda Sep 10 '15 at 21:21

1 Answers1

1

You might be able to use the components state for this.

getInitialState() {
    return {
        messagesLoaded: false,
        messages: [],
        threads: []
    }
},
onMessagesLoaded() {
    this.setState({
        messagesLoaded: true,
        messages: ChatWebAPIUtils.messages,
        threads: ChatWebAPIUtils.threads
    });
},
componentDidMount() {
    ChatWebAPIUtils.getAllMessages(this.onMessagesLoaded.bind(this))
},
render() {
    if(!this.state.messagesLoaded) {
        return (<div className="chatapp">Loading...</div>);
    }

    return (
        <div className="chatapp">
            <ThreadSection />
            <MessageSection />
        </div>
    );
}

You set the initial state to empty arrays for messages and threads. As soon as the component did mount, you call your async function and pass a callback function to it which will then update the state of the component - therefor causing the component (and the children which receive the new state) to update. As long as the data is not loaded - a "Loading" message is displayed.

It should also be possible to call your getMessages function in the componentWillMount() section.

I guess that the MessagesSection should only show the messages of the currently selected thread. I didn't take this into account for the example - so you'll have to add this logic somewhere too.

Edit: added information about preventing rendering of child components before data is loaded.

  • Hmmm. Well the problem is that currently MessageSection will try to get it's initial state from the store before getAllMessages has loaded the data into the store. Is it the convention to keep all state in the top level component or is there a way to make sure the store has been loaded first before MessageSection attempts to get it's initial state? – Nick Pineda Sep 10 '15 at 21:38
  • 1
    Okay, if you want to do it this way, you can simply handle this within the render() function of the ChatApp component. I updated my answer - you'll see a new messagesLoaded property in the state which is also updated within the callback function. – Patrick Hübl-Neschkudla Sep 10 '15 at 21:52
  • I'm soooo thankful for your help. Trying it out now. In this case though, does it mean that when "this.setState" is called that there will be an automatic update or do I need to be triggering some emit change from the store? – Nick Pineda Sep 10 '15 at 22:00
  • 1
    No problem :) As soon as you call this.setState() the component is going to update itself, so in this case you don't need to trigger anything else. If there are changes within your store - you might want to trigger the onMessagesLoaded function again, so you'll probably have to reference it somewhere in ChatWebAPIUtils when you pass it in the getAllMessages function. Or you implement something like ```ChatWebAPIUtils.onStoreChange(this.onMessagesLoaded.bind(this))``` – Patrick Hübl-Neschkudla Sep 10 '15 at 22:05