Nested Router, Nested Route는
route 안에 있는 또 다른 route이다.
price, chart 컴포넌트 각각 생성
v6 사용하시는 분들 nested routes 작성 방법이 달라진 것 같습니다.
https://ui.dev/react-router-nested-routes/
여기서 알기 쉽게 차근차근 설명해주네요
v6에서 nested routes를 구현하는 방법은 두 가지가 있습니다.
첫 번째는 부모 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"와 같이 써도 동작한다고 하네요.
두 번째 방법은 자식 route를 부모 element의 내부가 아닌 route 내부에 작성하는 방법입니다.
코드는 다음과 같이 작성합니다.
router.tsx에서
chart와 price 컴포넌트를 import하고
<Route path="/:coinId" element={<Coin />} >
<Route path="chart" element={<Chart />} />
<Route path="price" element={<Price />} />
</Route>
그리고 이 자식 Route들이 어디에 render 될지 부모의 element안에 Outlet을 이용해 표시해주면 됩니다.
Coin.tsx에서, react-router-dom에서 Outlet을 import하고
Overview와 Container 사이에 <Outlet />를 작성해주면 끝납니다.
<Link to={`/${coinId}/chart`}>Chart</Link>
<Link to={`/${coinId}/price`}>Price</Link>
<Routes>
<Route path="chart" element={<Chart />} />
<Route path="price" element={<Price />} />
</Routes>
useMatch()
React Router 5버전 => 6버전
useRouteMatch() => useMatch()
현재 위치를 기준으로 지정된 경로에 대한 일치 데이터를 반환합니다.
https://reactrouter.com/docs/en/v6/api#usematch
function Coin() {
const [loading, setLoading] = useState(true);
const { coinId } = useParams();
const state = useLocation().state as RouterState;
const [info, setInfo] = useState<InfoData>();
const [priceInfo, setPriceInfo] = useState<PriceData>();
const priceMatch = useMatch(`/:coinId/price`); 다른 url에서 작동시키면 null나옴
const chartMatch = useMatch(`/:coinId/chart`);
...
}
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;
}
`;
<Tabs>
<Tab isActive={chartMatch !== null}>
<Link to={`/${coinId}/chart`}>Chart</Link>
</Tab>
<Tab isActive={priceMatch !== null}>
<Link to={`/${coinId}/price`}>Price</Link>
</Tab>
</Tabs>
react-query
우리가 지금까지 작성해온 useState, useEffect(fetch())등을 fetcher function과 useQuery로 간편하게 작성가능
function Coins() {
const [coins, setCoins] = useState<ICoin[]>([]); 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); })(); }, []);
설치
queryClient, QueryClientProvider
index.js
import React from "react";
import ReactDOM from "react-dom";
import { QueryClient, QueryClientProvider } from "react-query";
import { ThemeProvider } from "styled-components";
import App from "./App";
import { theme } from "./theme";
const queryClient = new QueryClient();
ReactDOM.render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
</QueryClientProvider>
</React.StrictMode>,
document.getElementById("root")
);
2.api 관련된 코드를 컴포넌트와 분리한다.
src> api.ts 파일 생성
export function fetchCoins() {
return fetch("https://api.coinpaprika.com/v1/coins").then((response) =>
response.json()
);
}
api를 fetch하고 json하여 return하는 함수
3.useQuery
query의 고유 식별자, fetcher function 두 개의 인자가 필요
import useQuery from "react-Query"
const{ isLoading, data}=useQuery("allCoins", fetchCoins);
fetcher 함수가 loading 중이라면 reqct query가 isLoading을 통해서 true, false로 알려줌
fetch한 데이터를 data에 넣어줌
return (
<Container>
<Header>
<Title>코인</Title>
</Header>
{isLoading ? (
<Loader>"Loading.."</Loader>
) : (
<CoinsList>
{data?.slice(0, 100).map((coin) => ( //typescript가 data가 무엇인지 모름
<Coin key={coin.id}>
<Link
to={`/${coin.id}`}
state={{ name: coin.name }}
>
<Img
src={`https://cryptocurrencyliveprices.com/img/${coin.id}.png`}
/>
{coin.name}→
</Link>
</Coin>
))}
</CoinsList>
)}
</Container>
);
data와 그 안에 coin을 타입스크립트가 모르니까
const { isLoading, data } = useQuery<ICoin[]>("allCoins", fetchCoins); //InterfaceCoins으로 타입지정
react-query가 데이터를 캐시에 저장해두기 때문에
이 전과다르게 coins 페이지 -> coin페이지 -> 다시 coins페이지로 돌아올 때
Loading을 기다리지 않고 바로 정보 나옴
React Query
React 애플리케이션에서 서버 state를 fetching, caching, synchronizing, updating할 수 있도록 도와주는 라이브러리
"global state"를 건드리지 않고 React 및 React Native 애플리케이션에서 데이터를 가져오고, 캐시하고, 업데이트합니다.
```
// Create a client
const queryClient = new QueryClient()
// Provide the client to your App
QueryClientProvider client={queryClient}
```
https://react-query.tanstack.com/quick-start
Queries
쿼리는 서버에서 데이터를 가져오기 위해 모든 Promise 기반 메서드(GET 및 POST 메서드 포함)와 함께 사용할 수 있습니다. 제공한 고유 키는 애플리케이션 전체에서 쿼리를 다시 가져오고, 캐싱하고, 공유하는 데 내부적으로 사용됩니다. useQuery에서 반환된 쿼리 결과에는 템플릿 및 기타 데이터 사용에 필요한 쿼리에 대한 모든 정보가 포함되어 있습니다.
ex) const result = useQuery('todos', fetchTodoList)
https://react-query.tanstack.com/guides/queries
https://react-query.tanstack.com/reference/useQuery#_top
Query Key
React Query는 쿼리 키를 기반으로 쿼리 캐싱을 관리
https://react-query.tanstack.com/guides/query-keys
DevTools
react-query에서 제공하는 DevTools
render 할 수 있는 component이고 import 하면 캐시에있는 query를 볼 수있다.
app.tsx에서
import { ReactQueryDevtools } from "react-query/devtools";
function App() {
return (
<>
<GlobalStyle />
<Router />
<ReactQueryDevtools initialIsOpen={true} />
</>
);
}
Coin.tsx정리하기
fetch함수 api.ts에 정리하기
const BASE_URL =`https://api.coinpaprika.com/v1`;
export function fetchCoins() {
return fetch(`${BASE_URL}/coins`).then((response) =>
response.json()
);
}
export function fetchCoinInfo(coinId:string){
return fetch(`${BASE_URL}/coins/${coinId}`).then((response) =>
response.json()
);
}
export function fetchCoinTickers(coinId:string){
return fetch(`${BASE_URL}/tickers/${coinId}`).then((response) =>
response.json()
);
}
coin.tsx
const { isLoading: infoLoading, data: infoData } = useQuery(
["info", coinId], //식별자 구분하기위해
() => fetchCoinInfo(coinId!) //
이렇게 coinId 뒤에 !만 넣어줬더니 정상작동했습니다
!=> 확장 할당 어션셜로 값이 무조건 할당되어있다고 컴파일러에게 전달해 값이 없어도 변수를 사용할 수 있게 한다고 합니다.
);
const { isLoading: tickersLoading, data: tickersData } = useQuery(
["tickers", coinId],//식별자 구분하기위해
() => fetchCoinTickers(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>{tickersData?.total_supply}</span>
</OverviewItem>
<OverviewItem>
<span>Max Supply:</span>
<span>{tickersData?.max_supply}</span>
</OverviewItem>
</Overview>
'코딩 > Today I Learn' 카테고리의 다른 글
React Js Master #6 state management Recoil (0) | 2022.05.09 |
---|---|
React Js Master #5 Chart,React-ApexCharts,react-helmet,BrowserRouter deploy 할 때 (0) | 2022.05.04 |
React Js Master #3 createGlobalStyle , Link to, state, ,useLocation, datatypes (0) | 2022.04.20 |
React Js Master #2 Typescirpt ,interface, optional props, styled.d.ts (0) | 2022.04.12 |
React Js Master #1styled components ,themes (0) | 2022.04.11 |