Search code examples
javascriptarraysreactjsobjectreact-props

How do I resolve a Uncaught TypeError: Cannot convert null or undefined to object?


Can anyone help me resolve this Uncaught TypeError when trying to convert an object. This would be really helpful. I'm not sure where the problem is as I'm running out of variables to watch for in the debugger and variables to console.log. Any help would be appreciated.

As suggested, I've edited this problem to show the code and not pictures of the code.

Parent Component

import React, { Component } from "react";
import CatHeading from "./CatHeading";
import CatImage from "./CatImage";
import CatInfo from "./CatInfo";
import config from "../../config";

export default class Cat extends Component {
  // initialize state to hold fetch data for cats
  constructor(props) {
    super(props);
    this.state = {
      cats: [],
    };
    this.handleClick = this.handleClick.bind(this);
  }

  componentDidMount() {
    /* when the component is mounted, immediately preform a GET 
    request to /api/cats 
       to receive cat image and cat info */
    const requestOptions = {
      method: "GET",
      redirect: "follow",
    };

    fetch(`${config.REACT_APP_PORT_URL}/api/cats`, requestOptions)
      // use chained promises to receive the data from the server
      .then((response) => response.json())
      .then((responseJSON) => this.setState({ cats: responseJSON }))
      .catch((error) => console.log("error", error));
    // use this.setState to update state with response data
  }

  handleClick() {
    console.log("button clicked!");
  }

  // add a timeout method that adopts a dog every 5 seconds
  /* add a conditional rendering that will disable the adopt button 
  if it is not the users turn in the queue*/

  render() {
    const { cats } = this.state;
    // console.log(cats);
    /* figure out how to access these props for the children 
    components*/
    return (
      <div className="col-md text-center">
        <CatHeading cats={cats} />
        <CatImage cats={cats} />
        <CatInfo cats={cats} />
        <div className="mb-5">
          {/* add event handler to button to trigger DELETE /api/cats 
          dequeue a cat, this also will update the state*/}
          <button
            type="button"
            className="btn adopt-button"
            onClick={this.handleClick}
          >
            Adopt!
          </button>
        </div>
      </div>
    );
  }
}

// add prop types for the props that are passed down

Child Component

import React, { Component } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCaretLeft, faCaretRight } from "@fortawesome/free-solid-svg-icons";

export default class CatImage extends Component {
  render() {
    console.log(this.props.cats);
    const { cats } = this.pops;
    console.log(cats[0]);
    console.log(Object.keys(cats[0]));

    return (
      <div className="container">
        <div className="row">
          <div className="col-2 my-auto">
            <button type="button" className="btn btn-primary btn-sm caret">
              <FontAwesomeIcon icon={faCaretLeft} className="caret-left" />
            </button>
          </div>
          <div className="col-8">
            <img
              src="https://images.unsplash.com/photo-1598463166261-357c23778812?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1410&q=80"
              alt="A smiling golden-brown golden retriever listening to music"
            />
          </div>
          <div className="col-2 my-auto">
            <button type="button" className="btn btn-primary btn-sm caret">
              <FontAwesomeIcon icon={faCaretRight} className="caret-right" />
            </button>
          </div>
        </div>
      </div>
    );
  }
}

Console Logged Object with no null or undefined values Console.log(cats[0]) gives me an output of with an object of no undefined or null values. However, using Object.keys(cats[0]) produces an Uncaught TypeError: cannot convert null or undefined to object, even though the object that is logged to the console does not have any null or undefined values as far as I can tell.

Parent Component Direct Child Component No error before trying to access the keys from the first indexed object in the array

The error after trying to access the keys from the first indexed object in the array


Solution

  • Alright, so first let's run through what is happening here and then, later, we'll look at how you can mitigate such issues.

    Within the Parent component:

    export default class Cat extends Component {
      // initialize state to hold fetch data for cats
      constructor(props) {
        super(props);
        this.state = { cats: [] }; // Initially, "cats" is an empty array
      }
      // other...
    }
    

    Therefore when you do this:

    1. We get "undefined" because we do not have anything in the array.
      That should answer the first issue with your console.log().
    console.log(cats[0]); 
    
    // > undefined
    

    1. We get "Uncaught TypeError" because of the following reasons:
    • From ES2015 onwards, Object.keys() non-object argument will be coerced to an object.
    • Technically since "cats" is empty, Object.keys() finds no property names [in this case the index for the array] therefore "undefined" and thus it returns the TypeError.
    console.log(Object.keys(cats[0]));
    
    // > Uncaught TypeError: Cannot convert undefined or null to object
     
    

    Solution:

    Otherwise, the quickest thing to do would be to add extra check prior to what you want:

    
    // First, we make sure that "cats" is defined
    // Second, we ensure that "cats" is not empty
    cats && cats[0] && console.log(cats[0]);
    
    // Same for the other
    cats && cats[0] && console.log(Object.keys(cats[0]));
    

    If "cats", console.log() will simply not run.