Eucrow
Eucrow

Reputation: 47

"TypeError: Cannot convert undefined or null to object" rendering a nested JSON

I'm trying to render a nested JSON in react but I receive the error "TypeError: Cannot convert undefined or null to object" when I try to render the nested parts. Those parts are converted to objects when are stored in the state of the component by the componentDidMount() method, so I'm trying to use Object.keys() function. For expample, in the next JSON:

{
    "gear": 14,
    "valid": 1,
    "meteo": {
        "wind_direction": 152,
        "wind_velocity": 10.1
    },
}

When I try to render it using the Object.keys() function, like this:

const haul = this.state.haul
{Object.keys(haul.meteo).map( key => {
     return(<p>Sea state: {haul.meteo[key]} </p>)
})} 

the error is thrown in the Object.keys() line, and I don't understand why.

The complete component is this:

class ComponentsHaul extends Component {
    constructor(props) {
        super(props);
        this.state = { 
            haul: []
         };
        this.apiHaul = "http://myapi";
    }
    componentDidMount() {
        fetch(this.apiHaul)
            .then(response => {
                return response.json();
            })
            .then(haul => {
                this.setState(() => {
                    return {
                        haul
                    };
                });
            });
    }
    render() {
        const haul = this.state.haul
        return ( 
            <Fragment>
            <p>Gear: {haul.gear}</p>
            {Object.keys(haul.meteo).map( key => {
                return(<p>Sea state: {haul.meteo[key]} </p>)
                })}    
            </Fragment>
        );
    }
}

Upvotes: 1

Views: 190

Answers (4)

Drew Reese
Drew Reese

Reputation: 203257

haul is initially an array, there is no meteo, so Object.keys(haul.meteo) fails. You then later change the type (a no-no) to an object, keep the type consistent.

const state = { haul: [] };
console.log(Object.keys(state.haul.meteo));

If you change your initial state to provide an empty meteo object this should work for you on initial and subsequent renders while data is fetched.

this.state = {
  haul: {
    meteo: {},
  },
}

const state = { haul: { meteo: {} } };
console.log(Object.keys(state.haul.meteo));

Upvotes: 1

Piyush
Piyush

Reputation: 3285

This issue is coming on first render.

See, haul is state containing empty array []. When it renders first time, React tries to access meteo at line

Object.keys(haul.meteo)

But haul has no property named meteo which is undefined. Thus, React reads it like

Object.keys(undefined)

And you are getting this error. (componentDidMount runs after 1st render)

Solution: Instead check existence of meteo first, like this.

{
haul.meteo ? Object.keys(haul.meteo).map( key => {
                return(<p>Sea state: {haul.meteo[key]} </p>)
                })} : []
}

Upvotes: 0

Diyorbek Sadullaev
Diyorbek Sadullaev

Reputation: 477

You should first check if your haul state has meteo. When the component rendered your fetch data will not be ready yet. So, it is not safe to use property that doesn't exist yet.

render() {
  const haul = this.state.haul;

  if(!haul.meteo) {
    return <p>Loading...</p>
  }

  return (
    <Fragment>
      <p>Gear: {haul.gear}</p>
        {Object.keys(haul.meteo).map((key) => {
          return <p key={key}>Sea state: {haul.meteo[key]} </p>;
        })}
    </Fragment>
  );
}

And, of course, don't forget to add key prop while rendering array of React children.

Upvotes: 0

CevaComic
CevaComic

Reputation: 2114

This is because when you first render the component your state is empty.

Just add a condition:

{ hulu.meteo.length > 0 && Object.keys(haul.meteo).map( key => {
    return(<p>Sea state: {haul.meteo[key]} </p>)
})}  

Upvotes: 0

Related Questions