[스터디] #4 (크립토 트래커 클론) - API 연결

ch9eri·2022년 9월 27일
0

-> 보이지 않는 방식으로 데이터를 보내는 법

1. typescript - type 선언

interface CoinInterface {
  id: string;
  name: string;
  symbol: string;
  rank: number;
  is_new: boolean;
  is_active: boolean;
  type: string;
}
const [coins, setCoins] = useState<CoinInterface[]>([]);

-> coin으로된 array임을 알려주기

2. API 접근

useEffect : 특정 시기(시작, 끝, 변화할 때)에만 코드를 실행

useEffect(() => {
    (async () => {
      const response = fetch('https://api.coinpaprika.com/v1/coins');
      const json = await (await response).json();
      console.log(json);
    })();
  }, []);

엄청 많은 양의 데이터 가져옴 …

.slice 사용

setCoins(json.slice(0,100));
//100개만 가져오기

+) 로딩 기능 구현

const [loading, setLoading] = useState(true);

{loading ? (
        <Loader>Loading ... </Loader>
      ) : (
        <CoinsList>
          {coins.map((coin) => (
            <Coin key={coin.id}>
              <Link to={`/${coin.id}`}>{coin.name} &rarr;</Link>
            </Coin>
          ))}
        </CoinsList>
      )}

문제

coins → coin 이동시 가지고 있던 state 사라짐 → API re-fetch


Crypto Icon API

https://coinicons-api.vercel.app

코인 아이콘 가져오기

<Img
	src={`https://coinicons-api.vercel.app/api/icon/${coin.symbol.toLowerCase()}`}
/>


데이터 이동 방식

  1. parameter를 이용해 URL에게 정보 넘기기

  2. state 사용 (보이지 않는 방식으로 데이터를 보내는 법)

//Coins.tsx
<Link to={`/${coin.id}`} state={coin.name}>

+)

React Router 5버전
< Link to={{ pathname: "/home", state: state }} / >
React Router 6버전
< Link to="/home" state={state} / >

//Coin.tsx
interface RouteParams {
  coinId: string;
}
const { coinId } = useParams<{ coinId: string }>();
<Title>코인 {coinId}</Title>

좀 더 멋진 방 법 !

-> useLocation

react router DOM이 보내주는 location object에 접근

interface RouteState {
    state:{
        name: string;
    };
}
const {state} = useLocation() as RouteState;

<Title>코인 {state.name}</Title>

coins 화면을 먼저 거치고 coin으로 들어오면 [state.name](http://state.name) 표시됨

바로 coin으로 들어오면 name → undefined!


한줄로 줄이기

const response = fetch('https://api.coinpaprika.com/v1/${coinId}');
const json = await (await response).json();

->

const infoData = await (
        await fetch(`https://api.coinpaprika.com/v1/${coinId}`)
      ).json();
const [info, setInfo] = useState({});
const [priceInfo, setPriceInfo] = useState({});

useEffect(() => {
    (async () => {
      const infoData = await (
        await fetch(`https://api.coinpaprika.com/v1/${coinId}`)
      ).json();

      const priceData = await (
        await fetch(`https://api.coinpaprika.com/v1/${coinId}`)
      ).json();
      setInfo(infoData);
      setPriceInfo(priceData);
    })();
  }, []);

-> typescript는 info와 priceInfo를 빈 object로 인식한다 ^__^…

interface InfoData {
  id: string;
  name: string;
  symbol: string;
  rank: number;
  is_new: boolean;
  is_active: boolean;
  type: string;
  logo: string;
  tags: ITag[];
  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 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 [info, setInfo] = useState<InfoData>();
const [priceInfo, setPriceInfo] = useState<PriceData>();

타입정의 해줫다 알아묵어라~


useEffect ((){⇒},[]);

괄호 안에 아무 것도 안 넣으면 no dependency

: 컴포넌트의 시작에서만 실행

+) 괄호 안에 넣으면 바뀔 때마다 실행

지금 상황에서는 coinId가 컴포넌트 내에서는 변하지 않으므로 API request 한번만 실행


nested router

중첩된 라우터 (route 안에 있는 또 다른 route)

  1. 부모 route의 path 마지막에 /*를 적어 이 route의 내부에서 nested route가 render 될 수 있음을 표시하고 자식 route를 부모 route의 element 내부에 작성
//Router.tsx
<Route path="/:coinId/*" element={<Coin/>}/>
//Coin.tsx
<Routes>
        <Route path="chart" element={<Chart />} />
        <Route path="price" element={<Price />} />
</Routes>

Routes가 상대경로도 지원하기 때문에 path="chart"와 같이 써도 동작한다

  1. 자식 route를 부모 element의 내부가 아닌 route 내부에 작성
//Router.tsx
<Route path="/:coinId" element={<Coin />} >
<Route path="chart" element={<Chart />} />
<Route path="price" element={<Price />} />
</Route>
//Coins.tsx
		</CoinsList>
		 )}
	<Outlet />
</Container>

자식 Route들이 어디에 render 될지 부모의 element안에 Outlet을 이용해 표시

(import 잊지 말기!)


useMatch

react-router 6 이하 버전에서는 useReactMatch

특정한 URL에 있는지 여부를 알 수 있음

const priceMatch = useMatch('/:coinId/price');

해당 URL에 있으면 isExact: true object를 보내줌 (없으면 null)

<Tab isActive={chartMatch !== null}>
          <Link to={`/${coinId}/chart`}>Chart</Link>
        </Tab>

null 이 아니면 해당 URL에 있다는 뜻!

profile
잘하자!

0개의 댓글