javascriptreactjstypescriptjsx

Can't find the reason my app returns Uncaught TypeError: Cannot read properties of undefined


I'm using useState to store the data returned from an api:

function App() {
  const [{ ipAddressData, shouldLoadItems }, setModel] = useState<AppState>({
    ipAddressData: [],
    shouldLoadItems: true,
  });
  const [ipAddress, setIpAddress] = useState("");
  const ipDataUrl = `https://geo.ipify.org/${ipAddress}`;

  useEffect(() => {
    const fetchData = async () => {
      const response = await fetch(ipDataUrl);
      const data: IpAddress[] = await response.json();
      setModel({
        ipAddressData: data,
        shouldLoadItems: false,
      });
    };
    if (shouldLoadItems) {
      fetchData();
    }
  }, [shouldLoadItems]);


  function inputIp(e: { preventDefault: () => void }) {
    setModel((prevState) => {
      return {
        ...prevState,
        shouldLoadItems: true,
      };
    });
    e.preventDefault();
  }

Then i'm trying to return the data but i get the following error: Uncaught TypeError: Cannot read properties of undefined

  const { ip, location, domains, as, isp }: IpAddressData = ipAddressData;

  return (
    <div className="App">
      <main className={mainCss}>
        <h1 className={mainTitleCss}>IP Address Tracker</h1>
        <form className={searchBarContainerCss} onSubmit={inputIp}>
          <input
            type="text"
            placeholder="Search for any IP address or domain"
            className={searchInputCss}
            onChange={(e) => setIpAddress(e.target.value)}
          />
          <input type="submit" value="" className={searchBarButtonCss} />
        </form>
        <Results
          ip={ip}
          location={getLocation(
            location.city,
            location.country,
            location.postalCode
          )}
          timezone={location.timezone}
          isp={isp}
        />

function getLocation(city: string, country: string, postalCode: string) {
  return `${city}, ${country} ${postalCode}`;
}

Results.tsx:

interface IpAddressProps {
  ip: string;
  location: string;
  timezone: string;
  isp: string;
}

const Results: FC<IpAddressProps> = ({ ip, location, timezone, isp }) => {
  return (
    <div className={resultsContainerCss}>
      <div className={resultCss}>
        <div className={resultTitleCss}>IP Address</div>
        <div className={resultInfoCss}>{ip}</div>
      </div>
      <div className={resultCss}>
        <div className={resultTitleCss}>Location</div>
        <div className={resultInfoCss}>{location}</div>
      </div>
      <div className={resultCss}>
        <div className={resultTitleCss}>Timezone</div>
        <div className={resultInfoCss}>UTC {timezone}</div>
      </div>
      <div className={resultCss}>
        <div className={resultTitleCss}>ISP</div>
        <div className={resultInfoCss}>{isp}</div>
      </div>
    </div>
  );
};

export default Results;

index.ts where my interfaces are stored:

interface Location {
  country: string;
  region: string;
  city: string;
  lat: number;
  lng: number;
  postalCode: string;
  timezone: string;
  geonameId: number;
}
interface As {
  asn: number;
  name: string;
  route: string;
  domain: string;
  type: string;
}

export interface IpAddressData {
  ip: string;
  location: Location;
  domains: string[];
  as: As;
  isp: string;
}

export interface IpAddress {
  data: IpAddressData;
}

When i console.log ipAddressData it returns the data from the api as expected so i don't know what the problem is


Solution

  • I found the error, AppState interface had ipAddressData defined as an array when in reality it should be an object and that was the reason it was returning undefined.

    interface AppState {
      ipAddressData: IpAddressData[];
      shouldLoadItems: boolean;
    }
    

    Should be this instead:

    interface AppState {
      ipAddress: IpAddress | undefined;
      shouldLoadItems: boolean;
    }