1

I'm trying to get data from an API and put it into my state before rendering it on the component.

However, the empty state is keep being called before any value is being put into it.

Here's the code :

const cookies = new Cookies();

class ProductDetails extends Component {

state = {product:null}

componentWillMount(){
    this.getSelectedProduct()
}
getSelectedProduct(){
    var selected = cookies.get('SelectedProduct')
    Axios.get('http://localhost:1002/products/' + selected)
    .then(ok=>{
        console.log(ok.data)
        this.setState({product:ok.data})
        console.log(this.state.product[0].ProductName)
    })
}
renderContent(){
    console.log(this.state.product)
    if(this.state.product !== [] ){
        const {ProductName, ProductPrice, Description, Brand, Color, ScreenSize, RAM, Storage, Stock} = this.state.product[0]
        return([
            ProductName,
            ProductPrice,
            Description, 
            Brand, 
            Color, 
            ScreenSize, 
            RAM, 
            Storage, 
            Stock
        ])
    }
}
render(){
    return(
        <div >
            <h3>{this.renderContent().ProductName}</h3>

        </div>
    )
}
}
export default ProductDetails
CodeBoyCode
  • 2,227
  • 12
  • 29
Yosia
  • 69
  • 1
  • 8
  • change the condition to if(this.state.product && this.state.product.length ){ – Mohammed Ashfaq Sep 24 '18 at 09:38
  • 1
    I'm not 100% what the issue is, given that your code looks fine on first glance. However `this.setState` is asynchronous, so when you try to `console.log` the first product name, it's not there yet. –  Sep 24 '18 at 09:39
  • if you are thinking that console.log is not printing the correct value . `setState` is asynchronous – aravind_reddy Sep 24 '18 at 09:39
  • Yes, I also think the problem is because setState is asynchronous. And I'm looking for a way around it. And no, changing the if else condition that way won't work. It still give 'undefined' error. – Yosia Sep 24 '18 at 09:41
  • your code is alright. are you talking about the console.log after you did the set state? and `setState()` being async is not a problem, its a feature. you have to mould your app behaviour around it – Aseem Upadhyay Sep 24 '18 at 09:42

2 Answers2

2

That's the way React works – setState is asynchronous, it will not prevent the component from rendering before the state has been set, even if you do it from componentWillMount.

In your render method, check if product has been set and call renderContent only if it has.

You can show a "please wait" message or a spinner instead.

Example:

render() {
    if (this.state.product === null) {


    return(
        <div >
            <h3>{
                this.state.product === null ? 
                "loading data, please wait" :
                this.renderContent().ProductName
            }</h3>
        </div>
    )
}

Please note: componentWillMount is deprecated, and not available in more recent React versions. You should use componentDidMount instead.

Patrick Hund
  • 19,163
  • 11
  • 66
  • 95
1

I agree with previous answer that you should use the initial state as a model and therefore give the state product a value of an empty array. But, if you still want to define null in the initial state you can do a check before returning the render method.

render(){
  if (!Array.isArray(this.state.product) {
    return null // or something else such a loading icon or similar.
  }

  return(
    <div >
      Your rendered content here
      ...
    </div>
  )
}

See reference page of isArray: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray

brooksrelyt
  • 3,925
  • 5
  • 31
  • 54
J.Lindebro
  • 213
  • 3
  • 9