본문 바로가기
부트캠프교육중/react

[React] React query 적용 전 코드

by 뭉지야 2023. 8. 26.
728x90

index.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { ThemeProvider } from 'styled-components';
import { theme } from './theme';


const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <ThemeProvider theme={theme}>
    <App />
    </ThemeProvider>
  </React.StrictMode>
);

 

app.tsx

import React from 'react';
import styled, { createGlobalStyle } from "styled-components";
import Router from './Router';

const GlobalStyle = createGlobalStyle`
@import url('https://fonts.googleapis.com/css2?family=Hind&display=swap');
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, menu, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
main, menu, nav, output, ruby, section, summary,
time, mark, audio, video {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, main, menu, nav, section {
  display: block;
}
/* HTML5 hidden-attribute fix for newer browsers */
*[hidden] {
    display: none;
}
body {
  line-height: 1;
}
menu, ol, ul {
  list-style: none;
}
blockquote, q {
  quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
  content: '';
  content: none;
}
table {
  border-collapse: collapse;
  border-spacing: 0;
}
*{
  box-sizing: border-box;
}
body {
  font-family: 'Hind', sans-serif;
  background-color: ${(props)=> props.theme.bgColor};
  color: ${(props)=> props.theme.textColor}
}
a {
  text-decoration: none;
  color: inherit;
}
`;


function App() {
  return (
    <>
    <GlobalStyle />
    <Router/>
    </>
  );
}

export default App;

 

Router.tsx

import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
import Coins from "./routes/Coins";
import Coin from "./routes/Coin";
import Chart from "./routes/Chart";
import Price from "./routes/Price";


function Router(){
    return <BrowserRouter>
    <Routes>
        <Route path="/" element={<Coins />}/>
        <Route path="/:coinId" element={<Coin />} />
        <Route path="/:coinId/chart" element={<Chart />}/>
        <Route path="/:coinId/price" element={<Price />}/>
    </Routes>
    </BrowserRouter>
}
export default Router;

 

theme.ts

import { DefaultTheme } from "styled-components";

export const theme:DefaultTheme = {
    bgColor: "#1e272e",
    textColor: "#f5f6fa",
    accentColor: "#f53b57",
}

 

 

styled.d.ts

// import original module declarations
import 'styled-components';


// and extend them!
declare module 'styled-components' {
  export interface DefaultTheme {
    textColor: string;
    bgColor: string;
    accentColor: string;
    // borderRadius: string;


    // colors: {
    //   main: string;
    //   secondary: string;
    // };
  }
}

 

 

Chart.tsx

function Chart(){
    return <h1>Chart</h1>
}

export default Chart;

 

Coin.tsx

import { useEffect, useState } from "react";
import { useParams, useLocation } from "react-router";
import { Outlet, Link, useMatch } from "react-router-dom";
import { styled } from "styled-components";
import Chart from "./Chart";




interface RouterState {
    state: string;
}
interface IInfoData {
    id:"string";
    name:"string";
    symbol:"string";
    rank:"number";
    is_new:"boolean";
    is_active:"boolean";
    type:"string";
    logo:"string";
    // tags:"object";
    // team:"object";
    description:"string";
    message:"string";
    open_source:"boolean";
    started_at:"string";
    development_status:"string";
    hardware_wallet:"boolean";
    proof_type:"string";
    org_structure:"string";
    hash_algorithm:"string";
    // links:"object";
    // links_extended:"object";
    // whitepaper:"object";
    first_data_at:"string";
    last_data_at:"string";
}

interface IPriceData {
    id:"string"
    name:"string"
    symbol:"string"
    rank: "number"
    circulating_supply:"number"
    total_supply:"number"
    max_supply:"number"
    beta_value:"number"
    first_data_at:"string"
    last_updated:"string"
    quotes:{
        USD: {
            ath_date: string;
            ath_price: number;
            market_cap: number;
            market_cap_change_24h: number;
            percent_change_1h: number;
            percent_change_1y: number;
            percent_change_6h: number;
            percent_change_7d: number;
            percent_change_12h: number;
            percent_change_15m: number;
            percent_change_24h: number;
            percent_change_30d: number;
            percent_change_30m: number;
            percent_from_price_ath:number;
            price: number;
            volume_24h: number;
            volume_24h_change_24h: number;
        }
    }
}

const Container = styled.div`
/* border: 5px solid blue; */
    padding: 0px 20px;
    max-width: 480px;
    margin: 0 auto;
`;

const Header = styled.header`
    height: 10vh;
    display: flex;
    justify-content: center;
    align-items: center;
`;
const Title = styled.h1`
font-size: 48px;
    color: ${props => props.theme.accentColor}
`;

const Loader = styled.span`
    text-align: center;
    display: block;
`;
const Overview = styled.div`
    display: flex;
    justify-content: space-between;
    background-color: rgba(0,0,0,0.5);
    padding: 10px, 20px;
    border-radius: 10px;
`;
const OverviewItem = styled.div`
    display: flex;
    flex-direction: column;
    align-items: center;

    span:first-child {
        font-size: 10px;
        font-weight: 400;
        text-transform: uppercase;
        margin-bottom: 5px;
    }
`;
const Description = styled.p`
    margin: 20px 0px;
`;
const Tabs = styled.div`
    display: grid;
    grid-template-columns: repeat()(2, 1fr);
    margin: 25px 0px;
    gap: 10px;
`;

const Tab = styled.span<{ isActive: boolean }>`
    text-align: center;
    text-transform: uppercase;
    font-size: 12px;
    font-weight: 400;
    background-color: rgba(0,0,0,0.5);
    padding: 7px 0px;
    border-radius: 10px;
    color: ${props => props.isActive ? props.theme.accentColor : props.theme.textColor}
    a {
        display: block;
    }
`;
// react-router-dom v6부터 제네릭을 지원하지 않아서 interface이름으로 넣으면 오류가 난다....
/* interface RouteParams {
    coinId: string;
} */


function Coin(){
    const [loading, setLoading] = useState(true);
    const { coinId } = useParams<{coinId: string}>();
    /* const location = useLocation(); */
    /* console.log(location); */
     const {state} =  useLocation() as RouterState; 
    const [info, setInfo] = useState<IInfoData>();
    const [priceInfo, setPriceInfo] = useState<IPriceData>();
    const priceMatch = useMatch("/:coinId/price");
    const chartMatch = useMatch("/:coinId/chart");

   

  useEffect(()=> {
    (async ()=> {
        const response = await fetch(`https://api.coinpaprika.com/v1/coins/${coinId}`)
        const infoData = await response.json();
       const priceData = await (
        await fetch(`https://api.coinpaprika.com/v1/tickers/${coinId}`)
       ).json();
       console.log(infoData);
       console.log(priceData);
       setInfo(infoData);
       setPriceInfo(priceData);
       setLoading(false);
    })();
  }, [])

    return  (
      
    <Container>
    <Header>
    <Title>{state ? state : loading}</Title>
    </Header>
    {loading ? (<Loader>Loading...</Loader>
    ) : (
        <>
            <Overview>
                <OverviewItem>
                    <span>Rank:</span>
                    <span>{info?.rank}</span>
                </OverviewItem>
                <OverviewItem>
                    <span>Symbol:</span>
                    <span>${info?.symbol}</span>
                </OverviewItem>
                <OverviewItem>
                    <span>Open Source:</span>
                    <span>{info?.open_source ? "Yes" : "No"}</span>
                </OverviewItem>
            </Overview>
            <Description>{info?.description}</Description>
            <Overview>
                <OverviewItem>
                    <span>Total Supply:</span>
                    <span>{priceInfo?.total_supply}</span>
                </OverviewItem>
                <OverviewItem>
                    <span>Max Suply:</span>
                    <span>{priceInfo?.max_supply}</span>
                </OverviewItem>
            </Overview>

            <Tabs>
                <Tab isActive={chartMatch !== null}>
                    <Link to={`/${coinId}/chart`}>Chart</Link>
                </Tab>
                <Tab isActive={priceMatch !== null}>
                    <Link to={`/${coinId}/price`}>Price</Link>
                </Tab>
            </Tabs>
            
            
        </>
    )} 
    
  
    </Container>
    
    );
}
export default Coin;

 

Coins.tsx

import  styled  from "styled-components";
import {Link} from "react-router-dom"
import { useEffect, useState } from "react";


const Container = styled.div`
    padding: 0px 20px;
    max-width: 480px;
    margin: 0 auto;
`;

const Header = styled.header`
    height: 10vh;
    display: flex;
    justify-content: center;
    align-items: center;
`;

const CoinsList = styled.ul``;

const Coin = styled.li`
    background-color: white;
    color: ${props => props.theme.bgColor};
    padding: 20px;
    margin-bottom: 10px;
    border-radius: 10px;
    a {
        display: flex;
        align-items: center;
        padding: 20px;
        transition: color 0.4s ease-in;
       
    }
    &:hover {
        a {
            color: ${props => props.theme.accentColor}
        }
    }
`;

const Title = styled.h1`
font-size: 48px;
    color: ${props => props.theme.accentColor}
`;

const Loader = styled.span`
    text-align: center;
    display: block;
`;
const Img = styled.img`
    width: 35px;
    height: 35px;
    margin-right: 10px;
`;

interface CoinInterface {
        id:string;
        name: string;
        symbol: string;
        rank: number;
        is_new: boolean;
        is_active: boolean;
        type: string;
}

function Coins(){
    const [coins, setCoins] = useState<CoinInterface[]>([]);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        (async() =>{
        const response = await fetch("https://api.coinpaprika.com/v1/coins");
        const json = await response.json();
        setCoins(json.slice(0, 100))
        setLoading(false); 
       })(); 
    }, []);
    console.log(coins)

    return <Container>
    <Header>
    <Title>코인</Title>
    </Header>
    {loading ? (<Loader>Loading...</Loader>) 
    : (<CoinsList>
        {coins.map(coin => 
            <Coin key={coin.id}>
             <Link to={`/${coin.id}`} state={coin.name}>
        <Img 
        src={`https://coinicons-api.vercel.app/api/icon/${coin.symbol.toLowerCase()}`}
        />
        {coin.name} &rarr;
        </Link>
        </Coin>)
    }
    </CoinsList>)}
    </Container>
}
export default Coins;

 

Price.tsx

function Price(){
    return <h1>Price</h1>
}

export default Price;
728x90

'부트캠프교육중 > react' 카테고리의 다른 글

[React] React query 적용하기 2  (0) 2023.08.26
[React] React query 적용하기!!!  (0) 2023.08.26
[React] useRouteMatch, useMatch  (0) 2023.08.25
[React] theme, globalstyled  (0) 2023.08.24
[React] react-router-dom 6.4  (0) 2023.08.24