brandonhilkert
brandonhilkert

Reputation: 4475

Hold off React Render until after request

I have a component that's being loaded with the initial state:

  getInitialState: function() {
    return {
      panel: "deals",
      showExtension: false,
      person: {}

    };
  },

And I have this to find a person:

  componentDidMount: function() {
    this.findPersonFromBackground();
  },
  findPersonFromBackground: function() {
    chrome.runtime.sendMessage({ action: "findPerson", email: this.props.currentEmail.from_email }, function(person) {
      this.setState({person: person});
    }.bind(this));
  },

So all works fine. If the person is found, I render one thing, and if not, something else.

When the first is found, the view goes from non-found state to found state real fast, since the API calls are pretty quick.

What's the best way to wait to render until that first call comes back?

Upvotes: 5

Views: 4000

Answers (4)

top lan
top lan

Reputation: 11

maybe you need this project: https://github.com/toplan/react-hold

You can use the placeholder to keep the shape of your component.

Upvotes: 1

Michelle Tilley
Michelle Tilley

Reputation: 159105

There's no good way to do what you're asking, which is to keep a component from rendering at all until some asynchronous operation that the component initiates completes; the best you can do is use techniques from other answers to this question to render something slightly different in the case that it's not complete.

However, the problem you're actually trying to solve is preventing the brief flash of alternatively-rendered content if the API request starts and the completes very quickly. If you move the asynchronous operation to the parent of your component, you can ensure that the async operation completes before you ever even render your child.

If the Chrome runtime requests are consistently fast, this may be fine, but in the general case, it's worth considering what the behavior will be if the async operation takes a longer time to complete.

Upvotes: 5

Brigand
Brigand

Reputation: 86220

One way you can handle it is with the enum pattern. In this code, this.state.person is either the loading sentinel, the notFound sentinel, or the actual person.

var status = {loading: {}, notFound: {}};

// ...

getInitialState: function(){
  return {person: status.loading}
},
fetchPerson: function(){
  doFetchPerson(function(person){
    if (person) this.setState({person: person})
    else this.setState({person: status.notFound})
  }.bind(this))
},
render: function(){
  var person = this.state.person)
  if (person === status.loading) ...
  else if (person === status.notFound) ...
  else ...
}

Upvotes: 3

user217782
user217782

Reputation:

Is this more of a design question? You could show a spinner, an empty box, a shapes only preview (like Facebook feed), or nothing at all.

If this is a technical question then return null in render().

Upvotes: 1

Related Questions