Crypto Tracker 4.10 _ ReactQuery Part Two
46469 ワード
ReactQuery Part Two
useQeury hookではstateとfetch関数の代わりにfetcher()が使用されていることがわかります.
isLoadingとdataをパラメータとして
前者はfetcherが完了したかどうかをチェックし、データはjsonファイルを保存します.
画面を変えても、ReactQueryはAPIにアクセスしません.私たちが望んでいるデータがキャッシュされていることを知っているからです.これは大きな違いです.
だから「ローディング...」このような画面は見えません.
+)ヒント
reactiqueryにはいくつかのツールがあります.
Devtoolsは表示できるコンポーネントですが、これは何ですか?
React Queryのdev toolsをインポートすると、私のキャッシュのqueryが表示されます.
だからアプリはjsはDevtoolsをリフレッシュします.
では、このような窓口で確認できます.
queryのメソッドを視覚的に表示できます.
今Coinコードを整理します!
api.tsファイルで
2つの関数を作成します.
コインもtsxはfetch関数を使用しますが、同じキーは使用できません.
固有のキーを使用します.
配列にして
固有のidを持つ.
このように整列すると、{}にisLoadingを配置できますが、この場合
同じ名前で変えます.
コードの変更
2つのqueryをチェックできます...
多くの場所で子供がキャッシュに入るほど、リードキャッシュも少なくなります.
Coin.tsx
import { useEffect, useState } from 'react';
import { useQuery } from 'react-query';
import { Switch, Route, useLocation, useParams, useRouteMatch } from 'react-router';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
import Chart from './Chart';
import Price from './Price';
import { fetchCoinInfo, fetchCoinTickers } from './api';
//useParams 는 URL에서 관심있어 하는 정보를 잡아낼 수 있게 해준다.
function Coin() {
const { coinId } = useParams<RouteParams>();
const { state } = useLocation<RouteState>();
const priceMatch = useRouteMatch('/:coinId/price');
const chartMatch = useRouteMatch('/:coinId/chart');
const { isLoading: infoLoading, data: infoData } = useQuery<InfoData>(['info', coinId], () =>
fetchCoinInfo(coinId)
);
const { isLoading: tickersLoading, data: tickerData } = useQuery<PriceData>(
['tickers', coinId],
() => fetchCoinTickers(coinId)
);
//같은 키를 쓰면 좋지 않으므로 key가....여기서 멈춤
// 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();
// const priceData = await (
// await fetch(`https://api.coinpaprika.com/v1/tickers/${coinId}`)
// ).json();
// setInfo(infoData);
// setPriceInfo(priceData);
// setLoading(false);
// })();
// }, [coinId]);
const loading = infoLoading || tickersLoading;
return (
<Container>
<Header>
<Title>{state?.name ? state.name : loading ? 'Loading...' : infoData?.name}</Title>
</Header>
{loading ? (
<Loader>Loading...</Loader>
) : (
<>
<Overview>
<OverviewItem>
<span>Rank:</span>
<span>{infoData?.rank}</span>
</OverviewItem>
<OverviewItem>
<span>Symbol:</span>
<span>${infoData?.symbol}</span>
</OverviewItem>
<OverviewItem>
<span>Open Source:</span>
<span>{infoData?.open_source ? 'Yes' : 'No'}</span>
</OverviewItem>
</Overview>
<Description>{infoData?.description}</Description>
<Overview>
<OverviewItem>
<span>Total Suply:</span>
<span>{tickerData?.total_supply}</span>
</OverviewItem>
<OverviewItem>
<span>Max Supply:</span>
<span>{tickerData?.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>
<Link to={`/${coinId}/chart`}>Chart</Link>
<Link to={`/${coinId}/price`}>Price</Link>
{/* 다양한 URL 로 Switch 하기 */}
<Switch>
<Route path={`/${coinId}/price`}>
<Price />
</Route>
<Route path={`/${coinId}/chart`}>
<Chart />
</Route>
</Switch>
</>
)}
</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;
`;
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 }>`
//props 받기
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;
}
`;
export default Coin;
api.ts
const BASE_URL = `https://api.coinpaprika.com/v1`;
export async function fetchCoins() {
// const response = await fetch(`${BASE_URL}/coins`);
// const json = await response.json();
// return json;
//가독성 높은 오래된 방법
return fetch(`${BASE_URL}/coins`).then((response) => response.json());
// 두 코드는 같은 것이다. fetcher 함수인 fetchCoin 은 URL을 부르고 URL로 부터 json을 return 한다.
}
export async function fetchCoinInfo(coinId: string) {
//coinId 타입 명시 필요, coin Id를 fetch하는 함수
return fetch(`${BASE_URL}/coins/${coinId}`).then((response) => response.json());
}
export async function fetchCoinTickers(coinId: string) {
//coinId 타입 명시 필요 coin Ticker를 fetch하는 함수
return fetch(`${BASE_URL}/tickers/${coinId}`).then((response) => response.json());
}
App.js
import { createGlobalStyle } from 'styled-components';
import Router from './Router';
import { ReactQueryDevtools } from 'react-query/devtools';
export default function App() {
const GlobalStyle = createGlobalStyle`
@import url('https://fonts.googleapis.com/css2?family=Archivo+Narrow:wght@500&family=Bebas+Neue&family=Black+Han+Sans&family=Do+Hyeon&family=Source+Sans+Pro:wght@300;400&family=Ubuntu+Mono:ital@1&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, li {
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: 'Source Sans Pro', sans-serif;
//현재 App은 Theme안에 있으므로 Theme의 props에 접근 가능한 상태이다.
//그 말은 즉슨 이렇게 쓸 수 있다는 뜻이다
background-color: ${(props) => props.theme.bgColor};
color : ${(props) => props.theme.textColor};
}
a{
text-decoration: none;
color:inherit;
// 링크가 클릭되었을 때 너무 못생겨져서 부모로부터 상속받게 하여
// 색깔을 유지시켰다
}
`;
return (
<>
<GlobalStyle />
{/* 이것이 reset이고 기본값을 제거하는 방법이다. */}
<Router />
<ReactQueryDevtools initialIsOpen={true} />
{/* developer tool */}
</>
);
}
Reference
この問題について(Crypto Tracker 4.10 _ ReactQuery Part Two), 我々は、より多くの情報をここで見つけました
https://velog.io/@angel_eugnen/Crypto-Tracker-4.10-ReactQuery-Part-Two
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
import { useEffect, useState } from 'react';
import { useQuery } from 'react-query';
import { Switch, Route, useLocation, useParams, useRouteMatch } from 'react-router';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
import Chart from './Chart';
import Price from './Price';
import { fetchCoinInfo, fetchCoinTickers } from './api';
//useParams 는 URL에서 관심있어 하는 정보를 잡아낼 수 있게 해준다.
function Coin() {
const { coinId } = useParams<RouteParams>();
const { state } = useLocation<RouteState>();
const priceMatch = useRouteMatch('/:coinId/price');
const chartMatch = useRouteMatch('/:coinId/chart');
const { isLoading: infoLoading, data: infoData } = useQuery<InfoData>(['info', coinId], () =>
fetchCoinInfo(coinId)
);
const { isLoading: tickersLoading, data: tickerData } = useQuery<PriceData>(
['tickers', coinId],
() => fetchCoinTickers(coinId)
);
//같은 키를 쓰면 좋지 않으므로 key가....여기서 멈춤
// 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();
// const priceData = await (
// await fetch(`https://api.coinpaprika.com/v1/tickers/${coinId}`)
// ).json();
// setInfo(infoData);
// setPriceInfo(priceData);
// setLoading(false);
// })();
// }, [coinId]);
const loading = infoLoading || tickersLoading;
return (
<Container>
<Header>
<Title>{state?.name ? state.name : loading ? 'Loading...' : infoData?.name}</Title>
</Header>
{loading ? (
<Loader>Loading...</Loader>
) : (
<>
<Overview>
<OverviewItem>
<span>Rank:</span>
<span>{infoData?.rank}</span>
</OverviewItem>
<OverviewItem>
<span>Symbol:</span>
<span>${infoData?.symbol}</span>
</OverviewItem>
<OverviewItem>
<span>Open Source:</span>
<span>{infoData?.open_source ? 'Yes' : 'No'}</span>
</OverviewItem>
</Overview>
<Description>{infoData?.description}</Description>
<Overview>
<OverviewItem>
<span>Total Suply:</span>
<span>{tickerData?.total_supply}</span>
</OverviewItem>
<OverviewItem>
<span>Max Supply:</span>
<span>{tickerData?.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>
<Link to={`/${coinId}/chart`}>Chart</Link>
<Link to={`/${coinId}/price`}>Price</Link>
{/* 다양한 URL 로 Switch 하기 */}
<Switch>
<Route path={`/${coinId}/price`}>
<Price />
</Route>
<Route path={`/${coinId}/chart`}>
<Chart />
</Route>
</Switch>
</>
)}
</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;
`;
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 }>`
//props 받기
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;
}
`;
export default Coin;
const BASE_URL = `https://api.coinpaprika.com/v1`;
export async function fetchCoins() {
// const response = await fetch(`${BASE_URL}/coins`);
// const json = await response.json();
// return json;
//가독성 높은 오래된 방법
return fetch(`${BASE_URL}/coins`).then((response) => response.json());
// 두 코드는 같은 것이다. fetcher 함수인 fetchCoin 은 URL을 부르고 URL로 부터 json을 return 한다.
}
export async function fetchCoinInfo(coinId: string) {
//coinId 타입 명시 필요, coin Id를 fetch하는 함수
return fetch(`${BASE_URL}/coins/${coinId}`).then((response) => response.json());
}
export async function fetchCoinTickers(coinId: string) {
//coinId 타입 명시 필요 coin Ticker를 fetch하는 함수
return fetch(`${BASE_URL}/tickers/${coinId}`).then((response) => response.json());
}
import { createGlobalStyle } from 'styled-components';
import Router from './Router';
import { ReactQueryDevtools } from 'react-query/devtools';
export default function App() {
const GlobalStyle = createGlobalStyle`
@import url('https://fonts.googleapis.com/css2?family=Archivo+Narrow:wght@500&family=Bebas+Neue&family=Black+Han+Sans&family=Do+Hyeon&family=Source+Sans+Pro:wght@300;400&family=Ubuntu+Mono:ital@1&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, li {
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: 'Source Sans Pro', sans-serif;
//현재 App은 Theme안에 있으므로 Theme의 props에 접근 가능한 상태이다.
//그 말은 즉슨 이렇게 쓸 수 있다는 뜻이다
background-color: ${(props) => props.theme.bgColor};
color : ${(props) => props.theme.textColor};
}
a{
text-decoration: none;
color:inherit;
// 링크가 클릭되었을 때 너무 못생겨져서 부모로부터 상속받게 하여
// 색깔을 유지시켰다
}
`;
return (
<>
<GlobalStyle />
{/* 이것이 reset이고 기본값을 제거하는 방법이다. */}
<Router />
<ReactQueryDevtools initialIsOpen={true} />
{/* developer tool */}
</>
);
}
Reference
この問題について(Crypto Tracker 4.10 _ ReactQuery Part Two), 我々は、より多くの情報をここで見つけました https://velog.io/@angel_eugnen/Crypto-Tracker-4.10-ReactQuery-Part-Twoテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol