비트코인Detail 화면에 뿌려줄 데이터를 가져올 것이다.
코인의 정보와 가격을 가져올 것이다
현재 parameter에 존재하는 coinId를 이용할 것이다.
useEffect를 이용해 코드가 한번만 실행되게 할 것이다.API를 가져오고 이후 React Query로 더 간단하게 만들어 볼 것이다. 일단 만들자
뒷부분만 파라미터로 coinId 사용하도록바꿔준다.
좀 더 짧게 바꾸는 방법
await를 한줄에 쓸 수 있다.
await로 감싸주면 된다.
이것을 캡슐화라고 한다 ~!!
변수 두개를 쓸 바에 한번에 시간을 절약해서 작성할 수 있는 방법이다.
이제 받아올 priceData 변수를 만들자
두가지 정보를 받았다.
필요한 데이터가준비되었다. 이제 state 안에 붙여주자.
객체로 일단 {} 설정
그 안에 setInfo, setProceInfo를 설정해준다.
그 전에 typescript에 데이터 설명 위해 배울 것이 있다
데이터를 이용하기 위해
변수에 따로 저장해 놓으면 좋다
id : 로 시작해서 정의하고 싶지,
hardware_wallet: 으로 정의하고 싶지 않기 때문이다.
그 전에 interface두개를 만들어둔다.
이후 join으로 string이 나오는지 확인하여
interface에 복붙해주어 콤마 바로 앞에서 삭제 후 엔터를 누르면
모든 키들이 정렬되고 ctrl + shift + l 을 눌러 커서를 받아서 : 를 입력해준다.
이번에는 keys 대신 values를 받아올 것
value 의 type 을 map으로 하나씩 받아와 보자..
이제 key들과 매칭시키자.
모든 쉼표들을 클릭하고 엔터..
ctrl + shift + l 하고 커서로 전부 복붙한다.
여기서 주의할 점은 tags와 team 같은 경우에는 type 이 array인 것이다.
그래서 내부에 Itag라는 interface 를 하나 더 만들어줘야 한다.
그리고
ITag를 추가한다.
그 외의 object라고 써있는 친구들은 필요 없으니 지워주자..InfoData에서..
그리고 나서 typeScript의 type 들을 매칭시키기 위해
InfoData를 입력한다.
이제 info. 하면 자동완성으로 가져올 수 있는 것들을 확인할 수 있다.
PriceData도 마찬가지로 해주자.
첫번째 string 부터 복사하고 붙여넣는다..
여기서 중요한 친구는 quotes 밖에 없다.
이 친구는 내부에 값이 많으므로 따로 정의해줘야 한다.
number로 바꿔주는 것이다.
그리고
이렇게 붙여넣기 한 후 기본 값에 중괄호는 필요 없으니 제거해준다.
이제 qutoes.USD 하면 많은 것들을 확인할 수 있다.
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: 20vh;
display: flex;
justify-content: center;
align-items: center;
`;
const CoinsList = styled.ul``;
const Coin = styled.li`
background-color: white;
color: ${(props) => props.theme.bgColor};
margin-bottom: 10px;
padding: 20px;
border-radius: 15px;
a {
padding: 5px; // 좀 더 넓은 범위에서 transition 효과 적용 가능
transition: color 0.2s ease-in;
}
&:hover {
a {
color: ${(props) => props.theme.accentColor};
}
// 아래에서는 a가 아닌 Link라는 이름으로 사용했지만
// css에서는 anchor 를 선택해야 했다. 이건 모든 react router link들이
// 결국에는 anchor로 바뀔거기도 하고,
// react router dom이 우리 대신 설정을 도와줄 특별한 event listener들이 있기도 하다
}
`;
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: 25px;
height: 25px;
margin-right: 10px;
`;
interface CoinInterface {
id: string;
name: string;
symbol: string;
rank: number;
is_new: boolean;
is_active: boolean;
type: string;
}
function Coins() {
//state를 이용해 coins를 만들어 준다
const [coins, setCoins] = useState<CoinInterface[]>([]);
const [loading, setLoading] = useState(true);
//state가 coins으로 이루어진 array라는 것을 알려주기 위해<interface>작성
useEffect(() => {
(async () => {
const response = await fetch('https://api.coinpaprika.com/v1/coins');
const json = await response.json();
setCoins(json.slice(0, 100));
setLoading(false);
})();
}, []);
return (
<Container>
<Header>
<Title>코인</Title>
</Header>
{loading ? (
<Loader>"Loading..."</Loader>
) : (
//loading 이 참이면 Loading... 출력, 거짓이면 CoinsList 보여줌
<CoinsList>
{coins.map((coin) => (
<Coin key={coin.id}>
<Img
src={`https://cryptoicon-api.vercel.app/api/icon/${coin.symbol.toLowerCase()}`}
/>
<Link
to={{
pathname: `/${coin.id}`,
state: { name: coin.name },
//Link를 이용해 string 이외에 더 많은 데이터를 보낼 수 있다
}}
>
{coin.id}
</Link>
</Coin>
))}
</CoinsList>
)}
</Container>
);
}
export default Coins;
import styled from 'styled-components';
import { useEffect, useState } from 'react';
import { useLocation, useParams } from 'react-router';
//useParams 는 URL에서 관심있어 하는 정보를 잡아낼 수 있게 해준다.
function Coin() {
const [loading, setLoading] = useState(true);
//state가 coins으로 이루어진 array라는 것을 알려주기 위해<interface>작성
//Coins 에서 보내는 state 를 사용하기 위해 useLocation 이라는 hook을 사용해서 받아와보자! react-router-dom이 보내주는
// location object에 접근하기만 하면 된다.
const { coinId } = useParams<RouteParams>();
const { state } = useLocation<RouteState>();
useEffect(() => {
(async () => {
const infoData = await (await fetch(`https://api.coinpaprika.com/v1/coins/${coinId}`)).json();
const priceData = await (
await fetch(`https://api.coinpaprika.com/v1/tickers/${coinId}`)
).json();
setInfo(infoData);
setPriceInfo(priceData);
})();
}, []);
const [info, setInfo] = useState({});
const [priceInfo, setPriceInfo] = useState({});
return (
<Container>
<Header>
<Title>{state?.name || 'Loading'}</Title>
</Header>
{loading ? <Loader>"Loading..."</Loader> : null}
</Container>
);
}
interface RouteState {
name: string;
}
interface RouteParams {
coinId: string;
}
interface InfoData {
id: string;
name: string;
symbol: string;
rank: number;
is_new: boolean;
is_active: boolean;
type: string;
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;
first_data_at: string;
last_data_at: string;
}
interface PriceData {
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`
padding: 0px 20px;
max-width: 480px;
margin: 0 auto;
`;
const Header = styled.header`
height: 20vh;
display: flex;
justify-content: center;
align-items: center;
`;
const Title = styled.h1`
font-size: 50px;
color: ${(props) => props.theme.accentColor};
`;
const Loader = styled.span`
display: block;
text-align: center;
`;
export default Coin;