개발자 박가나
[241107 TIL] 본캠프 27일차 ('포켓몬 도감 만들기' 프로젝트 2일차) 본문
📌 오늘의 TIL
|
[포켓몬 도감] 프로젝트
기능 구현. 포켓몬 상세 정보 페이지
선택한 포켓몬의 상세 정보를 가져와서 화면에 보여준다.
포켓몬 선택 시 해당 포켓몬의 id 값을 router로 넘겨주고 path param을 이용해서 넘겨받은 id 값에 해당하는 포켓몬의 정보를 읽어온다.
/* Detail.jsx */
import { useParams } from 'react-router-dom';
import MOCK_DATA from '../Data';
import { useEffect, useState } from 'react';
export default function Detail() {
const param = useParams();
const [pokemon, setPokemon] = useState();
useEffect(() => {
setPokemon(MOCK_DATA.find((item) => item.id === Number(param.id)));
}, [param.id]);
return (
<>
{pokemon && (
<Wrap>
<Character src={pokemon.img_url} alt="character" />
<Name>{pokemon.korean_name}</Name>
<Description>
타입 :{' '}
{pokemon.types.map((type, index) => {
return index < pokemon.types.length - 1 ? `${type}, ` : type;
})}
</Description>
<Description>{pokemon.description}</Description>
<Button label="뒤로 가기" background="black" handleClick={() => navigate('/dex')} />
</Wrap>
)}
</>
);
}
기능 구현. 나만의 포켓몬 추가 및 삭제
특정 포켓몬을 나만의 포켓몬에 추가 및 삭제한다.
중복되지 않게, 최대 6개까지 등록 가능하게 제한한다.
/* Dex.jsx */
import Dashboard from '../components/Dashboard';
import PokemonList from '../components/PokemonList';
import MOCK_DATA from '../Data';
import { useState } from 'react';
export default function Dex() {
const [myPokemons, setMyPokemons] = useState([]);
/* 나만의 포켓몬 추가 이벤트 */
const handleAdd = (data) => {
// 이미 선택된 포켓몬인 경우
if (myPokemons.find((pokemon) => pokemon.id === data.id)) {
window.alert('이미 선택된 포켓몬입니다.');
}
// 이미 6개의 포켓몬이 선택된 경우
else if (myPokemons.length === 6) {
window.alert('더 이상 선택할 수 없습니다.');
} else {
setMyPokemons([...myPokemons, data]);
}
};
/* 나만의 포켓몬 삭제 이벤트 */
const handleDelete = (data) => {
setMyPokemons([...myPokemons.filter((pokemon) => pokemon.id !== data.id)]);
};
return (
<Wrap>
<Dashboard myPokemons={myPokemons} handleDelete={handleDelete} />
<PokemonList pokemons={MOCK_DATA} handleAdd={handleAdd} />
</Wrap>
);
}
/* Dashboard.jsx */
import PokemonBall from './PokemonBall';
import PokemonCard from './PokemonCard';
export default function Dashboard({ myPokemons, handleDelete }) {
return (
<Container>
<Title>나만의 포켓몬</Title>
<BallContainer>
{myPokemons.map((pokemon) => {
return <PokemonCard key={pokemon.id} type={'my'} pokemon={pokemon} handleDelete={handleDelete} />;
})}
{[...Array(6 - myPokemons.length)].map(() => {
return <PokemonBall key={index} />;
})}
</BallContainer>
</Container>
);
}
/* PokemonCard.jsx */
export default function PokemonCard({ type = 'list', pokemon, handleAdd, handleDelete }) {
/* 카드 내 버튼 클릭 이벤트 */
const handleClick = (e) => {
// 이벤트 버블링 방지
e.stopPropagation();
type === 'list' ? handleAdd(pokemon) : handleDelete(pokemon);
};
return (
<Container onClick={() => navigate(`/detail/${pokemon.id}`)}>
<Character src={pokemon.img_url} />
<Name>{pokemon.korean_name}</Name>
<Description>No. {String(pokemon.id).padStart(3, '0')}</Description>
<Button type="sub" bgcolor="red" label={type === 'list' ? '추가' : '삭제'} handleClick={handleClick} />
</Container>
);
}
react-toastify 적용
단순히 window alert이 아니라 react-toastify를 적용시켜 줌으로써 UI를 보완한다.
/* App.jsx */
import styled from 'styled-components';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
const Toast = styled(ToastContainer)`
.Toastify__toast {
background-color: #ffbe57;
border-radius: 8px;
font-size: 16px;
font-weight: 700;
color: #000000;
text-align: center;
padding: 10px 20px;
}
`;
function App() {
return (
<>
<Toast position="top-center" autoClose={2000} closeButton={false} hideProgressBar />
<Router />
</>
);
}
export default App;
/* Dex.jsx */
/* 나만의 포켓몬 추가 이벤트 */
const handleAdd = (data) => {
// 이미 선택된 포켓몬인 경우
if (myPokemons.find((pokemon) => pokemon.id === data.id)) {
toast('이미 선택된 포켓몬입니다.');
}
// 이미 6개의 포켓몬이 선택된 경우
else if (myPokemons.length === 6) {
toast('더 이상 선택할 수 없습니다.');
} else {
setMyPokemons([...myPokemons, data]);
}
};
warning 해결
console 창에 다음과 같은 warning이 떠있었다. error가 아닌 warning이었기 때문에 로직 수행에는 문제가 없었지만 그래도 해결해보자는 생각이 들었다.
해석을 해보면 개발자의 의도는 styled-components에 props로 넘겨주는 것이지만 DOM의 입장에서는 attribute로 받아들일 수 있기 때문에 구분이 필요하다는 것이었다.
props로 사용할 목적인 경우 변수명 앞에 $를 붙여주는 방법으로 해결 가능하다.
/* Button.jsx */
export default function Button({ type = 'main', bgcolor, label, handleClick }) {
return (
<Container type={type} $bgcolor={bgcolor} onClick={handleClick}>
{label}
</Container>
);
}
'내일배움캠프' 카테고리의 다른 글
[241111 TIL] 본캠프 29일차 ('포켓몬 도감 만들기' 프로젝트 4일차) (0) | 2024.11.11 |
---|---|
[241108 TIL] 본캠프 28일차 ('포켓몬 도감 만들기' 프로젝트 3일차) (0) | 2024.11.08 |
[241106 TIL] 본캠프 26일차 ('포켓몬 도감 만들기' 프로젝트 1일차) (0) | 2024.11.06 |
[241105 TIL] 본캠프 25일차 (0) | 2024.11.05 |
[241104 TIL] 본캠프 24일차 (0) | 2024.11.04 |