juni
SingleProject_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;
'프로젝트 > 미니 프로젝트' 카테고리의 다른 글
onboarding ( 로그인 , 회원가입 ) 세팅 (1) | 2024.10.08 |
---|---|
SingleProject_Next.js 포켓몬 도감 (0) | 2024.07.04 |
TeamProject_아웃소싱 프로젝트 (0) | 2024.06.21 |
TeamProject_아웃소싱 프로젝트 (0) | 2024.06.19 |
TeamProject_아웃소싱 프로젝트 (0) | 2024.06.19 |