Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React: initial render fires before state is set (via ajax)

I've set things up so the HomePage component renders UserShow for the current logged in user. For example, if a user with an ID of 2 is logged in and visits the HomePage page, it will render their UserShow.

The "normal" UserShow works correctly. For example if you type in /users/18, it will properly render. However it's not working when HomePage renders it.

I'm new to React (especially its lifecycle methods), so my debugging has been to throw alerts in at various steps. I'd say the most important findings to share are:

  1. currentUserID( ) is functioning and returns the correct ID
  2. Hard-coding the value of state.userID within componentDidMount causes things to work correctly

These two points lead me to believe that Render is being called before it can update state.userID with its (correct) return value. Even more specific is that it's rendering before the .success portion of the this.currentUserID() ajax call returns. If this is so, what's the best way to go about not doing an initial render until an ajax call like this completes?

My code is in a state of spaghetti - it's my first time doing front-end routing with JavaScript. I'm also managing sessions via using the user's email as the token in localStorage - I'm new to sessions in JS as well. Please bear with me.

HomePage component:

var HomePage = React.createClass({

getInitialState: function(){
    return{
        didFetchData: false,
        userID: null,
    }
},

componentWillMount: function(){
    newState = this.currentUserID()
    this.setState({userID: newState}) 
    // this.setState({userID: 2})   //hard-coding the value works
},

currentUserID: function(){
    if(App.checkLoggedIn()){
        var email = this.currentUserEmail()
        this.fetchUserID(email)
    }else{
        alert('theres not a logged in user')
    }
},

currentUserEmail: function(){
    return localStorage.getItem('email')
},

fetchUserID: function(email){ //queries a Rails DB using the user's email to return their ID
    $.ajax({
        type: "GET",
        url: "/users/email",
        data: {email: email},
        dataType: 'json',
        success: function(data){
            this.setState({didFetchData: 'true', userID: data.user_id})
        }.bind(this),
        error: function(data){
            alert('error! couldnt fetch user id')
        }
    })
},

render: function(){
    userID = this.state.userID
    return(
        <div>
            <UserShow params={{id: userID}} />
        </div>
    )
}
})

UserShow component:

var UserShow = React.createClass({

getInitialState: function(){
    return{
        didFetchData: false,
        userName: [],
        userItems: [],
        headerImage: "../users.png"
    }
},

componentDidMount: function(){
    this.fetchData()
},

fetchData: function(){  
    var params = this.props.params.id
    $.ajax({
        type: "GET",
        url: "/users/" + params,
        data: "data",
        dataType: 'json',
        success: function(data){
            this.setState({didFetchData: 'true', userName: data.user_name, userItems: data.items, headerImage: data.photo_url})
        }.bind(this),
        error: function(data){
            alert('error! couldnt load user into user show')
        }
    })
},

render: function(){
    var userItem = this.state.userItems.map(function(item){
        return <UserItemCard name={item.name} key={item.id} id={item.id} description={item.description} photo_url={item.photo_url} />
    })
    return(
        <div>
            <Header img_src={this.state.headerImage} />

            <section className="body-wrapper">
                {userItem}              
            </section>
        </div>
    )
}
})
like image 227
TCannadySF Avatar asked Dec 10 '25 22:12

TCannadySF


1 Answers

So what you want to do is to avoid rendering anything until your ajax-request returns your result.

You can do a check in the render method if the state is how you want it. If it's not, then return null, or a loader or some other markup. When the componentDidMount then sets the state, it will trigger a re-render, since the userID then is set, it will return the userShow component

Example:

render(){
  if(this.state.userID === null){
     return null; //Or some other replacement component or markup
  }

  return (
    <div>
        <UserShow params={{id: userID}} />
    </div>
  );

}

Fetching the data in the userShow component could be done like this:

componentWillReceiveProps(nextProps){
    //fetch data her, you'll find your prop params in nextProps.params
}

You can also avoid doing this here, by kicking the data-fetch in the render-method.

like image 126
thsorens Avatar answered Dec 13 '25 11:12

thsorens



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!