● 이론0_ index를 고유한 id로 지정하여, 랜더링한다면, 어떤 문제가 존재합니까?
○ 실습 구현 코드 :
- ReviewList.js
○ 실습 구현 코드 :
import "./ReviewList.css";
function ReviewItem({ key, item, onDelete }) {
//2_ ReviewItem 컴포넌트에서 받은 index를
const handleDelete = () => onDelete(item.id);
return (
<div key={key} className="ReviewListItem">
//3_ 가장 상위 태그의 key속성에 저장
<img
className="ReviewListItem-img"
src={item.imgUrl}
alt={item.title}
></img>
<div>
<h1>{item.title}</h1>
<p>{item.rating}</p>
<p>{formatDate(item.createdAt)}</p>
<p>{item.content}</p>
<button onClick={handleDelete}>삭제</button>
</div>
<input type="text"></input>//4_ input 태그를 추가해봤다.
</div>
);
}
function ReviewList({ items, onDelete }) {
return (
<ul>
{items.map((item) => {
//1_ 배열 프로퍼티 메소드인 map() 메소드에서, json의 각 index를 ReviewItem 컴포넌트에 전달
return <ReviewItem key={item.index} item={item} onDelete={onDelete} />;
})}
</ul>
);
}
export default ReviewList;
○ 알아야 할 개념 :
- json내의 고유속성 값 "id" : 배열 내 원소의 구성이 달라져도 변하지 않는 값
- json 배열의 index 값 : 배열 내 원소의 구성이 달라지면 , => index값도 변한다.
○ 위의 코드의 출력 결과 :
- 2번째 항목의 input 태그에 text를 적고, 1번째 항목을 삭제해보면, 그 다음 출력결과가 나온다.
- 삭제 후, 출력 결과 :
○ 결론 : 각 json 배열의 고유값은 배열의 프로퍼티로 설정해야 한다. (not index)
○ 해결 방법 코드 :
import "./ReviewList.css";
function ReviewItem({ key, item, onDelete }) {
//2_ ReviewItem 컴포넌트에서 받은 index를
const handleDelete = () => onDelete(item.id);
return (
<div key={key} className="ReviewListItem">
//3_ 가장 상위 태그의 key속성에 저장
<img
className="ReviewListItem-img"
src={item.imgUrl}
alt={item.title}
></img>
<div>
<h1>{item.title}</h1>
<p>{item.rating}</p>
<p>{formatDate(item.createdAt)}</p>
<p>{item.content}</p>
<button onClick={handleDelete}>삭제</button>
</div>
<input type="text"></input>//4_ input 태그를 추가해봤다.
</div>
);
}
function ReviewList({ items, onDelete }) {
return (
<ul>
{items.map((item) => {
//1_ 배열 프로퍼티 메소드인 map() 메소드에서, json의 각 id를 ReviewItem 컴포넌트에 전달
return <ReviewItem key={item.id} item={item} onDelete={onDelete} />;
})}
</ul>
);
}
export default ReviewList;
○ 그러면 고유 key_id를 설정하는 이유 :
위의 예시에서, 삭제 후 결과만 보고 추정을 해봤을 떄, "망고" 의 원소만 삭제됐다고 생각할 수 있지만,
"포도"를 삭제하고, "망고"원소를 "포도"로 수정하여, 결과가 나올 수도 있다.
그 과정을 제대로 알기 위해서는 고유key를 통해 추적을 할 수 있다.
◎ 연습문제 :
실습 설명
리액트에서 배열을 렌더링할 때는 key 라는 Prop을 지정해 주어야 한다고 배웠습니다.
FoodList 컴포넌트에서 key Prop을 올바로 적용해 봅시다.
○ 정답 코드 :
※주의 : key 프로퍼티 값은 내부 태그의 속성값으로 사용하면, 오류는 뜨지 않지만, 사용 X
- FoodList.js
function FoodListItem({ item, onDelete }) {
const { imgUrl, title, calorie, content } = item;
const handleDeleteFood = () => onDelete(item.id);
return (
<div>
<img src={imgUrl} alt={title} />
<div>{title}</div>
<div>{calorie}</div>
<div>{content}</div>
<button onClick={handleDeleteFood}>삭제</button>
</div>
);
}
function FoodList({ items, onDelete }) {
return (
//map메소드 사용할 때, item 매개인자 추가하여, 컴포넌트 프로퍼티 key에 item.id를 대입 적용하면 된다.
<ul>
{items.map((item) => {
return <FoodListItem key={item.id} item={item} onDelete={onDelete} />;
})}
</ul>
);
}
export default FoodList;
- 모범 FoodList.js 코드 :
function FoodList({ items, onDelete }) {
return (
<ul className="FoodList">
{items.map((item) => (
<li key={item.id}>//li태그에 key값 적용
<FoodListItem item={item} onDelete={onDelete} />
</li>
))}
</ul>
);
}
◎ 연습문제2 :
아래의 JSON 파일의 코드는 포켓몬 도감 151번까지의 포켓몬 데이터입니다.
각 데이터를 구분하는 값인 id, 포켓몬 이름을 값으로 하는 name 프로퍼티, 그리고 포켓몬의 속성인 types 프로퍼티가 있습니다.
[
{
"id": 1,
"name": "이상해씨",
"types": [
"풀",
"독"
]
},
{
"id": 2,
"name": "이상해풀",
"types": [
"풀",
"독"
]
},
...
]
○ 문제1_ [src]폴더 내 <App>, <Pocketmon> 컴포넌트를 작성해보시오.
- Pocketmon.js
//1_ 매개인자로 json의 원소 하나씩 전달받기
function Pocketmon({item}){
return (<div>
No.{item.id} : {item.name}//2_각 배열의 고유번호와 이름 출력
</div>);
}
export default Pocketmon;
- App.js
import items from '../pokemon.json';
import Pocketmon from './pocketmon.js';
function App(){
return (
<ul>
//★map()메소드로 렌더링 한다.
{items.map((item)=>{
<li key={item.id}>
<Pocketmon item={item} />
</li>
})}
</ul>
);
}
export default App;
○ 문제2_ '문제1'의 App 컴포넌트의 map을 변수로 저장하여, 작성해보기
import items from '../pokemon.json';
import Pocketmon from './pocketmon.js';
function App(){
const renderItems = items.map((item)=>{
<li key={item.id}>
<Pocketmon items={items} />
</li>
});
return (
<ul>
{renderItems}
</ul>
);
}
export default App;
○ 문제3_ '문제2'에서 sort 버튼 기능 적용해보기
배열 메소드의 sort 메소드를 사용하면 배열을 정렬할 수 있었죠?
이렇게 정렬한 배열을 렌더링 할 수 있었습니다.
아래 코드는 id 순서대로 / 반대로 정렬하는 예시입니다.
import { useState } from "react";
import items from "../pokemon.json";
import Pocketmon from "./Pocketmon.js";
function App() {
const [order, setOrder] = useState(1);
const handleIdAscSort = () => setOrder(1);
const handleIdDscSort = () => setOrder(-1);
const sortedItems = [...items].sort((a, b) => {
return order * (b.id - a.id);
});
const renderItems = sortedItems.map((item) => {
<li key={item.id}>
<Pocketmon items={items} />
</li>;
});
return (
<ul>
<button onClick={handleIdDscSort}>id 오름차순</button>
<button onClick={handleIdAscSort}>id 내림차순</button>
{renderItems}
</ul>
);
}
export default App;
○ 문제4_ '문제3'에서 filter프로퍼티 메소드를 이용하여, 삭제 기능 적용해보기
- App.js
import { useState } from "react";
import pocketmonitems from "../pokemon.json";
import Pocketmon from "./Pocketmon.js";
function App() {
const [order, setOrder] = useState(1);
const [items, setItems] = useState(pocketmonitems);
const handleIdAscSort = () => setOrder(1);
const handleIdDscSort = () => setOrder(-1);
const handleDelete = (id) => {
const deletePocketItems = items.filter((item) => item.id !== id);
setItems(deletePocketItems);
};
const sortedItems = [...items].sort((a, b) => {
return order * (b.id - a.id);
});
const renderItems = sortedItems.map((item) => {
<li key={item.id}>
<Pocketmon items={items} onDelete={handleDelete} />
</li>;
});
return (
<ul>
<button onClick={handleIdDscSort}>id 오름차순</button>
<button onClick={handleIdAscSort}>id 내림차순</button>
{renderItems}
</ul>
);
}
export default App;
- Pocketmon.js
function Pocketmon({ item, onDelete }) {
const handleDeletePocket = () => onDelete(item.id);
return (
<div>
No.{item.id} : {item.name}
<button onClick={handleDeletePocket}>삭제</button>
</div>
);
}
export default Pocketmon;