오늘의 나는 무엇을 배웠을까?
배열 렌더링을 하기 위해서 다음과 같이
//Child
import React from "react";
export default function Child({ name }) {
return <li>name : {name}</li>;
}
Child 컴포넌트를 만들고
//ShowName
import React from "react";
import Child from "./Child";
export Default ShowName() {
const names = ['a', 'b', 'c', 'd', 'e'];
return (
<div>
<h1>이름 목록</h1>
<ul>
<Child name={ names[0] }/>
<Child name={ names[1] }/>
...
</ul>
</div>
)
}
ShowName 컴포넌트를 만든 뒤
//App
import ShowName from "./ShowName";
export default App() {
return (
<div>
<ShowName />
</div>
)
}
ShowName 컴포넌트를 불러와 렌더링할 수도 있다. 하지만, 값을 내려주기 위해 일일이 작성하는 것은 매우 비효율적이다.
이때 사용할 수 있는 것이 배열의 map
메소드이다.
map
메소드는 배열의 메소드로써, 배열에 함수를 호출하여 새로운 배열을 만들게 된다.
배열의 각 요소에 한번씩 함수를 호출하고, 빈 요소에 대해서는 호출하지 않는다. 또, 기존 배열은 수정되지 않은채 새로운 배열을 만들게 된다.
map
메소드를 활용하면 다음과 같이 코드를 수정할 수 있다.
//ShowName
//...
export default ShowName() {
const names = ['a', 'b', 'c', 'd', 'e'];
return (
<div>
<h1>이름 목록</h1>
<ul>
{ names.map((name) => <Child name={name} />) }
</ul>
</div>
)
}
배열 요소마다 한번씩 함수를 호출하여 해당 요소를 prop으로 가진 Child 컴포넌트를 생성한다. 매우 간단해 졌다.
고로, 배열을 렌더링 할 떄는 map
메소드를 적극 활용하자.
배열의 sort
메소드를 통해 정렬해 줄 수 있다.
array.sort([compare function])
비교함수의 형식은
function compare(a,b) {
if (a is less than b by some ordering criterion) {
return -1;
}
if (a is greater than b by the ordering criterion) {
return 1;
}
// a must be equal to b
return 0;
}
과 같고, 두개의 매개변수를 받고 내부 로직을 실행하여 비교한다.
//ReviewList.js
function ReviewListItem({ item, onDelete }) {
const handleDeleteClick = () => onDelete(item.id);
return (
<div className="ReviewListItem">
<img className="ReviewListItem-img" src={item.imgUrl} alt={item.title} />
<div>
<h1>{item.title}</h1>
<p>{item.rating}</p>
<p>{formatDate(item.createdAt)}</p>
<p>{item.content}</p>
<button onClick={handleDeleteClick}>삭제</button>
</div>
</div>
);
}
function ReviewList({ items, onDelete }) {
return (
<ul>
{items.map((item) => {
return (
<li key={item.id}>
<ReviewListItem item={item} onDelete={onDelete} />
</li>
);
})}
</ul>
);
}
//App.js
function App() {
//~~~
const handleDelete = (id) => {
console.log(items);
const nextItems = items.filter((item) => item.id !== id);
setItems(nextItems);
};
//~~~
return (
<div>
<div>
<button onClick={handleNewestClick}>최신순</button>
<button onClick={handleBestClick}>베스트순</button>
</div>
<ReviewList items={sortedItems} onDelete={handleDelete} />
{hasNext && <button onClick={handleLoadMore}>더 보기</button>}
</div>
);
}
위와 같은 코드가 있다면, handleDelete
함수는 filter
메소드를 이용하여 item.id
값이 일치하지 않는 배열의 요소만 추출하여 새로운 배열을 만들게 된다. 즉, item.id
를 id 값으로 갖는 원소만 제거하는 것과 동일해 진다.
ReviewListItem
컴포넌트의 삭제 버튼이 클릭될 때 props로 받아올 onDelete
함수의 매개변수로 item.id
가 전달되어야 한다. onDelete
함수는 ReviewList
에서도 전달 받으며 그대로 ReviewListItem
컴포넌트에 전달된다.
배열을 렌더링 할 때, 의도하지 않는 방식의 결과가 나타나거나 비효율적인 동작을 막기 위해 key를 사용해야 한다.
가령, 아래와 같은 배열을
const array = ["a", "b", "c", "d"];
다음과 같이 렌더링 한다고 할 때
array.map(item => <div>{item}</div>);
b 와 c 사이에 새로운 요소 z를 삽입하게 되면,
<div>b</div>
와 <div>c</div>
사이에 새로운 태그를 삽입하는 것이 아닌, c → z / d → c / 마지막에 d 가 새로 삽입되는 과정으로 re-rendering이 일어나게 된다.
또한, a가 제거 되면 a→b / b→c / c→d / 마지막의 d가 제거되는 과정으로 re-rendenring이 일어나는 매우 비효율적인 동작을 하게 된다.
Key를 활용하면 이러한 불필요한 작업을 개선시킬 수 있는데,
[
{
id: 0,
text: 'a'
},
{
id: 1,
text: 'b'
},
{
id: 2,
text: 'c'
},
{
id: 3,
text: 'd'
}
];
위의 id와 같이 key로 사용할 수 있는 고유값이 객체에 있고,
array.map(item => <div key={item.id}>{item.text}</div>
와 같이 렌더링을 한다면 배열이 업데이트 되어도 수정되지 않는 기존 값은 그대로 두고 원하는 곳에 내용을 삽입하거나 삭제할 수 있게 된다. 훨씬 더 효율적인 렌더링을 할 수 있게 되는 것이다.
오늘의 나는 어떤 어려움이 있었을까?
사실 아직 props
state
에 대해 잘 모르겠다. React를 사용하며 가장 중요한 두 요소인 것 같은데, 내일 보충학습을 통해 제대로 알아보고 학습을 진행해야 겠다.
뭔가 느낌상 하나를 제대로 모른채로 넘어가니 뒷내용도 어영부영 되는 느낌이 있는 것 같은데, 시간을 조금 더 들여 이해하려고 노력해야겠다.
내일의 나는 무엇을 해야할까?