이 글은 ReactMasterClass강의를 보며 작성한 글입니다.
4월 4일에 작성됨
API를 사용해서 간단하게 비트코인을 보여주는 사이트를 만들어보면서 지금까지 배웠던 기술들을 총합해서 사용해보는 간단 프로젝트
import { BrowserRouter } from "react-router-dom";
npm i --save-dev @types/react-router-dom이런식으로 types를 다운받아주면 된다.<Route path="/:coinId">
<Coin />
</Route>
const { coinId } = useParams();이런식으로 작성을 하면 오류가 나는데, 이럴때는 TypeScript가 useParams가 비어있다고 인식해서 그렇다.const { coinId } = useParams<{ coinId: string }>();<>
<GlobalStyle />
<Router />
</>
const GlobalStyle = createGlobalStyle` //css코드 `;
(() => console.log("1"))()
useEffect(() => {
(async () => {
const response = await fetch("https://api.coinpaprika.com/v1/coins");
const json = await response.json();
setCoins(json.slice(0, 100));
setLoading(false);
})();
}, []);
const getCoins = async() =>{
const res = await axios("https://api.coinpaprika.com/v1/coins");
setCoins(res.data.slice(0, 100));
setLoading(false);
};
<Link
to={{
pathname: `/${coin.id}`,
state: { name: coin.name },
}}
>
const location = useLocation();
const { state } = useLocation<RouteStates>();
<Title>{state?.name || "이름 로딩중"}</Title>
const response = await (
await fetch(`https://api.coinpaprika.com/v1/coins/${coinId}`)
).json();
<Switch>
<Route path={`/:coinId/price`}>
<Price />
</Route>
<Route path={`/:coinId/chart`}>
<Chart />
</Route>
</Switch>
이런식으로 Route안에 Route를 추가해서 탭마다 하나만 보이게 만들 수도 있다.
React18 이후 일관성을 높이고, 사용자 혼동을 방지하기 위해 prop의 이름은 소문자나 앞에 $가 있어야만 사용자 지정 속성으로 인식한다. $가 추가된 이유는 일부 라이브러리 또는 구성 요소는 대문자를 사용하는 prop을 요구하기 때문이다.
const priceMatch = useRouteMatch("/:coinId/price")
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);
})();
}, []);
만약에 이 코드를 React-Query를 사용하게 되면 어떻게 될까?
일단 fetcher라는 함수를 만들어줘야 한다.
fetcher는 api.ts라는 파일을 하나 만들어서 관리하게 된다.
fetcher함수는 fetch를 하는 함수라고 보면 된다.
const response = await fetch("https://api.coinpaprika.com/v1/coins");
const json = await response.json();
이 부분을 때와서
export async function fetchCoins() {
return await fetch("https://api.coinpaprika.com/v1/coins").then((response) =>
response.json(),
);
}
만들어주고
그 다음부터 useQuery라는 hook을 사용하면 되는데 useQuery는 2가지 인자가 필요하다.
잠깐 정리해보면
useQuery가 fetcher함수를 불러주고 isLoading값에 로딩중인지 알려주고 data에 fetcher에서 return한 json을 넣어준다!
그래서 그 긴거를..
const { isLoading, data } = useQuery<ICoin[]>("allCoins", fetchCoins);
단 한 줄로 해결할 수 있다..
심지어 React-Query를 사용하면 가져온 데이터를 캐싱해두기 때문이다.
그래서 코인을 누르고 뒤로가기를 눌러도 loading이 뜨지 않는다!
심지어 많은 state들도 사라지기에 사용할 때 너무 편하다!
또한 캐싱된 데이터를 보기 위해서
<ReactQueryDevtools initialIsOpen={true} />
를 사용하여 시각화해 확인할 수도 있다.
ReactQuery는 Key를 Array로 받기 때문에 key을 줄 때 중복이 된다면,
const {} = useQuery(["info", coinId], () => fetchCoinInfo(coinId));
const {} = useQuery(["tickers", coinId], () => fetchCoinTickers(coinId));
이런식으로 Array로 Key를 만들어주면 고유한 값을 가지게 할 수 있다.
이제 우리가 useQuery에서 하나씩 뽑아 먹을 때, 여러 개의 쿼리를 날리면 프로퍼티 이름이 겹칠 것이다.
그렇때는 아래처럼 이름을 지정해서 사용해주면 된다.
const { isLoading: infoLoading, data: infoData } = useQuery(
["info", coinId],
() => fetchCoinInfo(coinId),
);
const { isLoading: tickersLoading, data: tickerData } = useQuery(
["tickers", coinId],
() => fetchCoinTickers(coinId),
);
또한 이놈들도 infoData의 type과 tickerData의 type을 모르기에 타입 지정을 해줘야한다.
const { isLoading: infoLoading, data: infoData } = useQuery<InfoData>(
["info", coinId],
() => fetchCoinInfo(coinId),
);
const { isLoading: tickersLoading, data: tickerData } = useQuery<PriceData>(
["tickers", coinId],
() => fetchCoinTickers(coinId),
);
![[Pasted image 20240404080950.png]]
그러면 이런식으로 캐시에 들어가게 된다.
const [loading, setLoading] = useState(true);
const [info, setInfo] = useState<InfoData>();
const [priceInfo, setPriceInfo] = useState<PriceData>();
useEffect(() => {
(async () => {
const infoData = await (
await fetch(`https://api.coinpaprika.com/v1/coins/${coinId}`)
).json();
console.log(infoData);
setInfo(infoData);
const priceData = await (
await fetch(`https://api.coinpaprika.com/v1/tickers/${coinId}`)
).json();
setPriceInfo(priceData);
setLoading(false);
})();
}, []);
그래서 결론을 보면 위에서 아래로 바뀐 것을 볼 수 있다!
const { isLoading: infoLoading, data: infoData } = useQuery<InfoData>(
["info", coinId],
() => fetchCoinInfo(coinId),
);
const { isLoading: tickersLoading, data: tickerData } = useQuery<PriceData>(
["tickers", coinId],
() => fetchCoinTickers(coinId),
);
const loading = infoLoading || tickersLoading;
React-Helmet이라는 것을 사용해서 head를 넣을 수 있다.