user11371080
user11371080

Reputation:

TypeError: Cannot read property 'image' of undefined React state

I am pulling data from an internal API that returns an image that I am trying to display. I have a page function that is set to display the information from that response. The response is as follows for a single bottle

{
   "data":{
      "id":"cc134653-c463-4e79-8b9e-f52dfe02498e",
      "type":"bottle",
      "attributes":{
         "name":"Luc Belaire Rare Luxe",
         "price":"$29.99",
         "image":"https://cdn11.bigcommerce.com/s-7a906/images/stencil/1000x1000/products/10929/10518/Luc-Belaire-Rare-Luxe__73902.1555086630.jpg?c=2",
         "sku":"813497005010",
         "size":"750ML",
         "origination":"France",
         "varietal":"Sparkling Wine"
      }
   }
}

I am new to React so I could be mistaken here, but this function is intended to display a loader, just a simple fontawesome spinner, until the data is available.

Here is the function

import React, { useState, useEffect } from 'react';
import { Loader } from '../../components/Loader'
import { endpoints } from "../../utils/fetch";

export default function Bottle({ match }) {
  let [bottle, setBottle] = useState([])

  useEffect(() => {
    fetch(`${endpoints().bottle}/${match.params.sku}`, { method: "GET" })
    .then(response => response.json())
    .then(data => setBottle(data.data))
  }, [])

  return (
    !bottle ? <Loader /> :
    <div className="card">
      <div className="card-body">
        <div className="row">
          <div className="mb-4 mb-lg-0 col-lg-6">
            <div>
              <div className="position-relative h-sm-100 overflow-hidden">
                <img className="img-fluid fit-cover w-sm-100 h-sm-100 rounded cursor-pointer" 
                  src={bottle.attributes.image} 
                  alt={bottle.attributes.name} 
                  height={200}
                  width={125} />
              </div>
            </div>
          </div>
          <div className="d-flex justify-content-between flex-column col-lg-6">
            <div>
              <h5>Apple iMac Pro (27-inch with Retina 5K Display, 3.0GHz 10-core Intel Xeon W, 1TB SSD)</h5>
              <a className="fs--1 mb-2 d-block" href="#!">Computer &amp; Accessories</a>
              <h4 className="d-flex align-items-center"><span className="text-warning mr-2">$1199.50</span></h4>
              <p className="fs--1 mb-1"><span>Shipping Cost: </span><strong>$50</strong></p>
              <p className="fs--1">Stock: <strong className="text-success">Available</strong></p>
            </div>
            <div className="row">
              <div className="col-auto">
                <button type="button" className="border-300 mr-2  btn btn-outline-danger btn-sm">
                  <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="heart" className="svg-inline--fa fa-heart fa-w-16 mr-1" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
                    <path fill="currentColor" d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z" />
                  </svg>
                  282
                </button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}

I would expect, based on what I've written, that the loader would display until the bottle has is not null. Instead, I get the following error.

enter image description here

Am I misusing state here? I've tried to use async and await here but was not successful in getting the data to display. Just the same error.

Upvotes: 0

Views: 260

Answers (1)

Ben Wainwright
Ben Wainwright

Reputation: 4651

Your default value for bottle is [], in JavaScript, an empty array is still considered truthy, so this will result in your component following the else path before your data has finished loading.

Also, when you are rendering the component, you are not using the data as an array anyway.

Change your useState to this:

let [bottle, setBottle] = useState();

This will provide a default value of undefined to bottle, which should mean your ternary will behave correctly.

Upvotes: 1

Related Questions