위코드에서 공부하며 정리한 내용입니다.
동적 라우팅을 직접 구현해보는 과제입니다. 페이지 구성과 구현해야 할 동작은 아래 이미지와 같습니다.
몬스터 카드가 나열된 리스트 형식의 Monsters 컴포넌트에서 해당 몬스터를 클릭 했을 때 /detail/[몬스터 id] 로 이동하게 합니다. (path parameter 사용) 이동한 몬스터 디테일 페이지에서 해당 url 의 path parameter 값을 추출하고 그 값을 활용해 해당 페이지의 api 를 호출해 화면에 그려줍니다.
라우터의 디테일 페이지 경로에 path parameter 를 추가하고,
<Routes>
<Route path="/" element={<Monsters />} />
<Route path="/detail/:id" element={<Monster />} />
</Routes>
Monsters 컴포넌트에서 useEffect 를 활용해 전체 데이터를 받고 CardList 컴포넌트에 props 로 전달합니다.
import React, { useState, useEffect } from "react";
import CardList from "./CardList/CardList";
import "./Monsters.scss";
function Monsters() {
const [monsters, setMonsters] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => res.json())
.then((res) => setMonsters(res)); // 데이터 받을 때 콘솔 찍어보기!
}, []); // 의존성 배열을 비워서 한번만 데이터를 받아옴
return (
<div className="monsters">
<h1>Assignment - Dynamic Routing</h1>
<CardList monsters={monsters} />
</div>
);
}
export default Monsters;
CardList 컴포넌트에서 props 로 받은 데이터를 map 함수로 돌려 리스트 화면을 만듭니다. 이때 Link 를 사용해서 /detail/몬스터 ID] 로 이동하게 연결합니다.
import React from "react";
import { Link } from "react-router-dom";
import "./CardList.scss";
function CardList({ monsters }) {
return (
<div className="cardList">
{monsters.map(({ id, name, email }) => {
return (
<Link key={id} to={`/detail/${id}`}>
<div className="card">
<img
src={`https://robohash.org/${id}?set=set2&size=180x180`}
alt="user avatar"
/>
<h2>{name}</h2>
<p>{email}</p>
</div>
</Link>
);
})}
</div>
);
}
export default CardList;
이렇게 이동한 Monster 페이지에서 useParams 훅을 사용해 해당 url 의 path parameter 값을 추출하고 (id 값) 그 값으로 해당 몬스터의 데이터를 받습니다. (fetch 의 api 주소의 패스파라미터 값에 id 값을 넣어서)
import React, { useState, useEffect } from "react";
import { useParams, Link } from "react-router-dom";
import Card from "../../components/Card/Card";
import "./Monster.scss";
function Monster() {
const params = useParams(); // 현재 url 의 pathparameter 값 추출
const userId = params.id; // 해당 id 값을 변수에 저장
const [monster, setMonster] = useState({});
useEffect(() => {
fetch(`https://jsonplaceholder.typicode.com/users/${userId}`) // userId 값으로 데이터 호출
.then((res) => res.json())
.then((res) => setMonster(res));
}, [userId]); // 이후 버튼 액션을 고려해 userId 를 의존성 배열에 넣음
return (
<article className="monster">
<div className="btnWrapper">
<button>Back to Monsters List</button>
</div>
<Card monster={monster} /> // 카드 이미지는 별도 컴포넌트 그려줌
<div className="btnWrapper">
<button>Previous</button>
<button>Next</button>
</div>
</article>
);
}
export default Monster;
디테일 페이지 monster 컴포넌트에서 Link 를 활용해 Back to Monster List 버튼을 누르면 "/" 경로로 이동하고, Preview, Next 버튼을 누르면 각각 이전 다음 몬스터에 대한 정보가 나오게 합니다. 기능 추가는 바로 위 코드에서 Link 만 추가하면 됩니다.
import React, { useState, useEffect } from "react";
import { useParams, Link } from "react-router-dom";
import Card from "../../components/Card/Card";
import "./Monster.scss";
function Monster() {
const params = useParams();
const userId = params.id;
const [monster, setMonster] = useState({});
useEffect(() => {
fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
.then((res) => res.json())
.then((res) => setMonster(res));
}, [userId]); // 이전 이후 버튼으로 이동시 userId 변화에 따라 데이터를 새로 받아옴
return (
<article className="monster">
<div className="btnWrapper">
<Link to={"/"}>
<button>Back to Monsters List</button>
</Link>
</div>
<Card monster={monster} />
<div className="btnWrapper">
<Link to={`/detail/${parseInt(userId) - 1}`}>
<button>Previous</button>
</Link>
<Link to={`/detail/${parseInt(userId) + 1}`}>
<button>Next</button>
</Link>
</div>
</article>
);
}
export default Monster;
카드 컴포넌트 코드
import React from "react";
import "./Card.scss";
function Card({ monster }) {
const { id, name, email } = monster;
return (
<div className="card">
<img
src={`https://robohash.org/${id}?set=set2&size=180x180`}
alt="user avatar"
/>
<h2>{name}</h2>
<p>{email}</p>
</div>
);
}
export default Card;