본문 바로가기

카테고리 없음

SingleProject_240627 ( TS-Favorite-Countries )

📦src
 ┣ 📂api
 ┃ ┗ 📜countryAPI.ts
 ┣ 📂assets
 ┃ ┗ 📜react.svg
 ┣ 📂components
 ┃ ┣ 📜CountryCard.tsx
 ┃ ┣ 📜CountryList.tsx
 ┃ ┗ 📜Layout.tsx
 ┣ 📂pages
 ┃ ┗ 📜Home.tsx
 ┣ 📂routes
 ┃ ┗ 📜Router.tsx
 ┣ 📂types
 ┃ ┗ 📜country.ts
 ┣ 📂zustand
 ┃ ┗ 📜countryStore.ts
 ┣ 📜App.css
 ┣ 📜App.tsx
 ┣ 📜index.css
 ┣ 📜main.tsx
 ┣ 📜QueryClientSetup.tsx
 ┗ 📜vite-env.d.ts

 

import axios from "axios";
import { Country } from "../types/country";

export const countryApi = axios.create({
});

export const getCountries = async (): Promise<Country[]> => {
  try {
    const response = await countryApi.get("/all");
    return response.data;
  } catch (error) {
    throw new Error();
  }
};

 

import { Country } from "../types/country";

const CountryCard = ({
  country,
  handleToggleCountry,
}: {
  country: Country;
  handleToggleCountry: (country: Country) => void;
}) => {
  return (
    <div
      className="flex flex-col border rounded gap-2 px-4"
      onClick={() => handleToggleCountry(country)}
    >
      <div className="flex justify-center mb-2">
        <img
          src={country.flags.svg}
          alt={`${country.name.common} flag`}
          className="w-32 h-20"
        />
      </div>
      <h2>{country.name.common}</h2>
      <p>{country.capital}</p>
    </div>
  );
};

export default CountryCard;

 

import { useQuery } from "@tanstack/react-query";
import { getCountries } from "../api/countryAPI";
import CountryCard from "./CountryCard";
import { useEffect } from "react";
import { Country } from "../types/country";
import useCountryStore from "../zustand/countryStore";

const CountryList = ({ isDone }: { isDone: boolean }) => {
  const { countries, setCountries, favoriteCountries, setFavoriteCountries } =
    useCountryStore();

  const { data, error, isLoading } = useQuery({
    queryKey: ["countries"],
    queryFn: getCountries,
  });

  const handleToggleCountry = (country: Country) => {
    if (isDone) {
      setFavoriteCountries(
        favoriteCountries.filter((toggle) => toggle.cca3 !== country.cca3)
      );
      setCountries([country, ...countries]);
    } else {
      setCountries(countries.filter((toggle) => toggle.cca3 !== country.cca3));
      setFavoriteCountries([country, ...favoriteCountries]);
    }
  };

  useEffect(() => {
    if (data) {
      setCountries(data);
    }
  }, [data, setCountries]);

  if (isLoading) {
    return (
      <div className="flex justify-center items-center">
        <h1 className="text-4xl">로딩중입니다!!</h1>
      </div>
    );
  }

  if (error) {
    return (
      <div className="flex justify-center items-center">
        <h1 className="text-4xl">에러가 발생했습니다!</h1>
      </div>
    );
  }

  const isDoneCountries = isDone ? favoriteCountries : countries;

  return (
    <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-8 px-8">
      {isDoneCountries.map((country) => (
        <CountryCard
          key={country.cca3}
          country={country}
          handleToggleCountry={handleToggleCountry}
        />
      ))}
    </div>
  );
};

export default CountryList;

 

import CountryList from "../components/CountryList";

const Home = () => {
  return (
    <>
      <h1 className="flex justify-center items-center mt-8 mb-8 text-2xl font-bold">
        Favorite Countries
      </h1>
      <CountryList isDone={true} />
      <h1 className="flex justify-center items-center mb-8 text-3xl font-bold">
        Countries
      </h1>
      <CountryList isDone={false} />
    </>
  );
};

export default Home;

 

import { createBrowserRouter } from "react-router-dom";
import Home from "../pages/Home";

const Router = createBrowserRouter([
  {
    path: "/",
    element: <Home />,
  },
]);

export default Router;

 

export interface Country {
  name: {
    common: string;
    official: string;
    nativeName: {
      [key: string]: {
        official: string;
        common: string;
      };
    };
  };
  tld: string[];
  cca2: string;
  ccn3: string;
  cca3: string;
  cioc: string;
  independent: boolean;
  status: string;
  unMember: boolean;
  currencies: {
    [key: string]: {
      name: string;
      symbol: string;
    };
  };
  idd: {
    root: string;
    suffixes: string[];
  };
  capital: string[];
  altSpellings: string[];
  region: string;
  subregion: string;
  languages: {
    [key: string]: string;
  };
  translations: {
    [key: string]: {
      official: string;
      common: string;
    };
  };
  latlng: number[];
  landlocked: boolean;
  area: number;
  demonyms: {
    eng: {
      f: string;
      m: string;
    };
  };
  flag: string;
  maps: {
    googleMaps: string;
    openStreetMaps: string;
  };
  population: number;
  gini: {
    [key: string]: number;
  };
  fifa: string;
  car: {
    signs: string[];
    side: string;
  };
  timezones: string[];
  continents: string[];
  flags: {
    png: string;
    svg: string;
  };
  coatOfArms: {
    png?: string;
    svg?: string;
  };
  startOfWeek: string;
  capitalInfo: {
    latlng: number[];
  };
  postalCode: {
    format: string;
    regex: string;
  };
}

 

import { create } from "zustand";
import { Country } from "../types/country";

const useCountryStore = create<{
  countries: Country[];
  setCountries: (countries: Country[]) => void;
  favoriteCountries: Country[];
  setFavoriteCountries: (favoriteCountries: Country[]) => void;
}>((set) => ({
  countries: [],
  setCountries: (countries) => set({ countries }),
  favoriteCountries: [],
  setFavoriteCountries: (favoriteCountries) => set({ favoriteCountries }),
}));

export default useCountryStore;

 

import { RouterProvider } from "react-router-dom";
import Router from "./routes/Router";

function App() {
  return <RouterProvider router={Router} />;
}

export default App;

 

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import QueryClientSetup from "./QueryClientSetup.tsx";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <QueryClientSetup>
      <App />
    </QueryClientSetup>
  </React.StrictMode>
);

 

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { ReactNode } from "react";

const queryClient = new QueryClient();

const QueryClientSetup = ({ children }: { children: ReactNode }) => {
  return (
    <QueryClientProvider client={queryClient}>
      {children}
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
};

export default QueryClientSetup;