Richard
Richard

Reputation: 8955

React - can't render state value

I have the following React component.

import React, { Component } from 'react';

class VesselDropdown extends Component {

    constructor(props) {
        super(props)    
        this.state = {
        }
    }

    componentDidMount() {
        fetch('http://localhost:3001/get/vessels')
        .then(res => res.json())
        .then((data) => {
          this.setState({ 
              vessels: data,
              test: 'xyz'
            })
        })
        .catch(console.log)
    }

    render() {
        console.log(this.state);
        return (
          <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
            <h1>Move the mouse around!</h1>
            <p>The current mouse position is ({this.state.test})</p>
            {
                this.state.vessels.map(d => {
                    console.log(d);
                    return <div>test</div>
                })
            }
          </div>
        );
      }
}

export default VesselDropdown;

I am trying to render some data that is stored in the state. But I get the following errors:

Cannot read property 'map' of undefined

If I remove the following code, I get no errors, and it displays the 'xyz' in the browser.

        {
            this.state.vessels.map(d => {
                console.log(d);
                return <div>test</div>
            })
        }

Question:

Do you know what I am doing incorrectly? Why cannot I not get the vessels data from the state?

Thanks

More info:

GET http://localhost:3001/get/vessels

returns:

[{"vesselimo":9337626,"vesselname":"NYK CONSTELLATION"}]

Upvotes: 0

Views: 402

Answers (3)

G_S
G_S

Reputation: 7110

Set initial state of vessels in your constructor.

For example

this.state={
  vessels:[]
}

This helps the map method to execute without throwing an undefined error

Upvotes: 2

Hezron Kimutai
Hezron Kimutai

Reputation: 21

Since componentDidMount runs a synchronous function, means the component will be rendered even before the data is available, so try adding a condition to the code block like this.

    this.state.vessels && this.state.vessels.map(d => {
        console.log(d);
        return <div > test < /div>
    })
}

Don't forget to initialise vessels as an empty array i.e

this.state={
vessels:[]
}

This will ensure that the block would be rendered only when you have some data in the state.

Upvotes: 1

norbitrial
norbitrial

Reputation: 15166

The reason behind that is fetch is asynchronously returning the response for your array, meanwhile render tries to call map() on your non existing array. Which is happening faster than the response arrives. That's why you got that error message on the console.

You need to define maybe an empty array for vessels state in the constructor. Or checking before calling map() if the value is undefined or null by using && operator.

Adding default empty array solution - map() can be called on an empty array:

constructor(props) {
   super(props);

   this.state = {
      vessels: []
   };
}

Or in the render checking with && operator:

{
   this.state.vessels && this.state.vessels.map(d => {
      console.log(d);
      return <div>test</div>
   })
}

Both will do the job for you.

+1 suggestion:

Once you do map() you need to add for each returned element in render a key for the wrapper element. So my suggestion would be to add as the following:

this.state.vessels && this.state.vessels.map((d, index) => {
   return <div key={index}>test</div>
})

I hope that helps!

Upvotes: 2

Related Questions