Fetch data from the APIs

Our components have state now, but the data is still hardcoded. Let’s replace it with real data from the APIs we set up earlier. To do this, we need a way to run code after a component renders. That is what the useEffect hook does, and we used it in the Counter app.

Fetching countries in SelectCountry

Open src/components/select-country.tsx. We will remove the hardcoded country list and fetch it from the REST Countries API instead.

import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { Label } from "@/components/ui/label";
import { useEffect, useState } from "react"; // πŸ‘€ Add useEffect
import type { CountryData } from "@/types/country";
import { fetchCountries } from "@/services/countries"; // πŸ‘€

const SelectCountry = () => {
  const [country, setCountry] = useState<CountryData | null>(null); // πŸ‘€ Start with null
  const [countryData, setCountryData] = useState<CountryData[]>([]); // πŸ‘€ Start empty

  // πŸ‘€ Fetch country list when the component mounts
  useEffect(() => {
    fetchCountries().then((data) => setCountryData(data));
  }, []);

  const handleOnCountryChange = (value: string) => {
    const selected = countryData.find((c) => c.code === value);
    setCountry(selected!);
  };

  return (
    <div className="w-full space-y-12">
      <h1 className="text-6xl">Covid Statistics</h1>
      <div className="flex flex-col gap-5 justify-start w-full">
        <Label className="text-xl">Select a country:</Label>
        <Select value={country?.code} onValueChange={handleOnCountryChange}>
          <SelectTrigger className="w-full text-xl p-8 bg-white">
            <SelectValue placeholder="Country..." />
          </SelectTrigger>
          <SelectContent>
            {countryData.map((c) => (
              <SelectItem value={c.code} key={c.code}>
                {c.name}
              </SelectItem>
            ))}
          </SelectContent>
        </Select>
      </div>
    </div>
  );
};

export default SelectCountry;

The key change is useEffect. As you saw in the Counter app, it takes a function to run (the effect) and an array of dependencies. The empty [] means β€œrun this effect once, when the component first mounts.” When the fetch completes, we update countryData with the real list.

We also changed the initial state: country starts as null (no selection yet) and countryData starts as an empty array (nothing loaded yet). Notice the country?.code. The optional chaining returns undefined instead of throwing when country is null.

Fetching Covid data in DisplayStatistics

Open src/components/display-statistics.tsx. We will fetch the Covid statistics for the US as a default.

import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { useEffect, useState } from "react"; // πŸ‘€ Add useEffect
import type { CovidData } from "@/types/covid";
import { fetchCovidData } from "@/services/disease"; // πŸ‘€

const STATISTICS: (keyof CovidData)[] = ["confirmed", "active", "recovered"];

const DisplayStatistics = () => {
  const [covidData, setCovidData] = useState<CovidData | null>(null); // πŸ‘€ Start with null

  // πŸ‘€ Fetch covid data for the US when the component mounts
  useEffect(() => {
    fetchCovidData("US").then((data) => setCovidData(data));
  }, []);

  return (
    <div className="flex w-full justify-between flex-col sm:flex-row gap-5">
      {STATISTICS.map((statistic) => (
        <Card className="w-full" key={statistic}>
          <CardHeader>
            <CardTitle className="capitalize">{statistic}</CardTitle>
          </CardHeader>
          <CardContent className="flex justify-end items-center gap-2">
            <Avatar>
              <AvatarImage src={covidData?.countryFlag} />
              <AvatarFallback>
                {covidData?.countryCode.toUpperCase()}
              </AvatarFallback>
            </Avatar>
            <div className="text-2xl">
              {covidData?.[statistic]?.toLocaleString()}
            </div>
          </CardContent>
        </Card>
      ))}
    </div>
  );
};

export default DisplayStatistics;

This uses the same pattern: useEffect with [] to fetch once on mount. Since covidData starts as null, we use optional chaining (?.) when reading its properties, so reading a property returns undefined instead of throwing before the data arrives.

Run the app. The dropdown should now show every country in the world, and the cards should show real US statistics. Selecting a different country does not update the cards yet. We will fix that in the next section.

Checkpoint: Commit your progress.

git add .
git commit -m "dashboard-06: Fetch country and covid data from APIs"
git push