简体   繁体   中英

Why does Appbase return 'undefined' before returning the correct item?

I am playing around with AppBase.io as a database and I don't understand why this component renders twice after getting an item? It seems as if the AppBase "database" returns undefined first and the correct item afterwards. Does anybody know why? Check out the example, where I get 2 logs.

In this component I need to get an item from the database and render it (nothing special).

Thanks for explaining.

 var appbaseRef = new Appbase({ url: "https://JHtFTzy4H:d083068b-596c-489d-9244-8db2ed316e79@scalr.api.appbase.io", app: "winwin" }); class ItemFull extends React.Component { constructor() { super(); this.state = { item: '' } } componentWillMount() { appbaseRef.get({ type: "items", id: 'AWI5K7Q-5Q83Zq9GZnED' }).on('data', (response) => { this.setState({ item: response }); }).on('error', function(error) { console.log(error) }) } render() { console.log(this.state.item._id, '<-- console_log'); return (<div></div>); } } ReactDOM.render(<ItemFull /> , document.getElementById('root')); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/appbase-js/2.2.11/appbase.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="root"></div> 

That's because appbaseRef.get is asynchronous. ItemFull does its initial rendering and you see first console.log

this.state.item._id at the first time actually checks for _id in a String Object ("") which is of course undefined.

Then this.setState is called in componentWillMount (which has already been mounted and rendered at least once) and updates the state forcing component to rerender because of state change.

This is why you see 2 console logs.

You cant prevent render() hook from executing but you can make sure that you render your content only when the data has arrived:

render() {
  console.log(this.state.item._id, '<-- console_log');

  if(this.state.item) { // this will check if item is different than ''
    return ( 
      <div> 

      </div>
    )
  }
}

one more thing. Use componentDidMount for initial data fetching as it's the recommended place.

It doesn't. The asynchronous call you start in componentWillMount doesn't hold up the rendering process (which is good), so render is called before the call completes, so this.state.item._id is undefined because at that point, this.state.item is '' (the value you set in your constructor).

This is perfectly normal, just ensure that you render the component in an appropriate way for when it doesn't have the information yet, eg:

 var appbaseRef = new Appbase({ url: "https://JHtFTzy4H:d083068b-596c-489d-9244-8db2ed316e79@scalr.api.appbase.io", app: "winwin" }); class ItemFull extends React.Component { constructor() { super(); this.state = { item: '' } } componentWillMount() { appbaseRef.get({ type: "items", id: 'AWI5K7Q-5Q83Zq9GZnED' }).on('data', (response) => { this.setState({ item: response }); }).on('error', function(error) { console.log(error) }) } render() { return <div>{this.state.item._id ? this.state.item._id : "loading..."}</div>; } } ReactDOM.render(<ItemFull />, document.getElementById('root')); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/appbase-js/2.2.11/appbase.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="root"></div> 

If the component shouldn't be rendered at all before you have that information, then you're requesting it in the wrong place. :-) You'd request it in the parent component and only render this component when you have the information (passing the information in props ), something like this:

 var appbaseRef = new Appbase({ url: "https://JHtFTzy4H:d083068b-596c-489d-9244-8db2ed316e79@scalr.api.appbase.io", app: "winwin" }); class ParentComponent extends React.Component { constructor(...args) { super(...args); this.state = {item: null}; } componentWillMount() { appbaseRef.get({ type: "items", id: 'AWI5K7Q-5Q83Zq9GZnED' }).on('data', (response) => { this.setState({ item: response }); }).on('error', function(error) { console.log(error) }) } render() { return this.state.item ? <ItemFull item={this.state.item} /> : <div>loading...</div>; } } class ItemFull extends React.Component { render() { return <div>{this.props.item._id}</div>; } } ReactDOM.render( <ParentComponent /> , document.getElementById('root')); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/appbase-js/2.2.11/appbase.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="root"></div> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM