Seth Mario
Seth Mario

Reputation: 13

Cannot read property 'updated' of undefined

When I use setApi(data.time); in the fetch section I can normally do console.log(api.updated);, but why I can not do just like what I wrote in the code below?

CodeSandbox

import React, { useState, useEffect } from "react";
import "./styles.css";

export default function App() {
  const [api, setApi] = useState({});

  useEffect(() => {
    fetch("https://api.coindesk.com/v1/bpi/currentprice.json")
      .then((res) => res.json())
      .then((data) => {
        setApi(data);
      });
  }, []);
  console.log(api.time.updated);

  return (
    <div className="App">
      <h1>Currency Exchange</h1>
      {/* <p>Time: {api.time.updated}</p>
      <u>
        <li>Code: {api.bpi.USD.code}</li>
        <li>Rate: {api.bpi.USD.rate}</li>
      </u> */}
    </div>
  );
}

Upvotes: 1

Views: 87

Answers (3)

Marbin274
Marbin274

Reputation: 509

you can use a extra state to check loading data and display it when fetch done:

export default function App() {
  //add loading to check api request and data to save result
  const [api, setApi] = useState({ loading: false, data: undefined });

  useEffect(() => {
    setApi({ loading: true });
    fetch("https://api.coindesk.com/v1/bpi/currentprice.json")
      .then((res) => res.json())
      .then((data) => {
        setApi({ loading: false, data: data });
      });
  }, []);


  return (
    <>
      {!api.loading && api.data ? (//check if data is loaded
        <div className="App">
          <h1>Currency Exchange</h1>
          <p>Time: {api.data.time.updated}</p>
          <u>
            <li>Code: {api.data.bpi.USD.code}</li>
            <li>Rate: {api.data.bpi.USD.rate}</li>
          </u>
        </div>
      ) : (
        <>Loading data...</>//show a message while loading data(or <></> if want not display something)
      )}
    </>
  );
}

Upvotes: 0

Tony Drummond
Tony Drummond

Reputation: 375

Your revised code:

import React, { useState, useEffect } from "react";
import "./styles.css";

export default function App() {
  const [api, setApi] = useState({});

  useEffect(() => {
    fetch("https://api.coindesk.com/v1/bpi/currentprice.json")
      .then((res) => res.json())
      .then((data) => {
        setApi(data);
      });
  }, []);

  return (
    <div className="App">
      <h1>Currency Exchange</h1>
      {api.time?.updated && <><p>Time: {api.time.updated}</p>
      <u>
        <li>Code: {api.bpi.USD.code}</li>
        <li>Rate: {api.bpi.USD.rate}</li>
      </u></>}
    </div>
  );
}

Simple way to check and ensure the API resolved with the appropriate data, then show the designated information.

Note that there's a reason why you have .then for your API query - it takes time to come back. Your code was executing before the api state could be filled with the response.

With the logical && operator, there's a simple way to look at it with React and I use it all the time.

If I have a loader component or something I want to show only when the loading variable is true, I can do something like this:

{loading && <Loader />}

The code to the right of the && will ONLY run if the left side is true. Since it's AND, if the first part is false it doesn't matter what the other parts are and they're skipped.

Upvotes: 0

Tholle
Tholle

Reputation: 112917

Before the request is complete api will be an empty object. api.time will then be undefined, and trying to access property updated on that will give rise to your error.

You could use the logical AND && operator to make sure api.time is set.

const [api, setApi] = useState({});

useEffect(() => {
  fetch("https://api.coindesk.com/v1/bpi/currentprice.json")
    .then((res) => res.json())
    .then((data) => {
      setApi(data);
    });
}, []);

console.log(api.time && api.time.updated);

Upvotes: 1

Related Questions