서로 같은 UI를 가지고 다른 data를 보여주는 컴포넌트는 원하는 만큼 재사용할 수 있다. 인스타그램의 피드 아이템이나 쇼핑 어플리케이션의 상품 목록과 같은 것이 이런 컴포넌트에 해당한다. 이 때 이러한 컴포넌트가 정확하게 몇 개 사용될지를 미리 정해서 hard coding 하는 것이 아니라, 동적으로 리스트를 업데이트한다.
forEach vs. map | 왜 map을 사용해야 하는가?
jsx 구문에서 컴포넌트를 요소로 갖는 배열을 전달했을 때, 리액트는 이것을 간단히 렌더링할 수 있다. 이 때 forEach 구문은 배열의 모든 아이템에 콜백 함수를 실행할 뿐이고, map 구문은 모든 아이템에 콜백 함수를 실행한 뒤에 그 아이템들로 이루어진 새로운 배열을 반환한다.
map
// this is working!
props.expenses.map((item) => (
<ExpenseItem
title={item.title}
amount={item.amount}
date={item.date}
/>
)
forEach
forEach 구문에서 배열을 반환하면 map과 동일하게 이용할 수 있지 않을까?
그 전에 jsx 구문의 culry braces 안에서 표현식을 작성할 수 있다고 했었는데, expression(표현식)과 statement의 차이점을 알아보자.
There are subtle differences between expressions and statements in JavaScript. An expression is a block of code that evaluates to a value. A statement is any block of code that is performing some action(statement는 보다 포괄적인 개념으로 expression을 포함하는 개념이다).
값을 평가하지 않는 선언문은 표현식이 아니기 때문에, 아래와 같은 코드를 { curly braces } 안에서 작성할 수 없다.
// this is not working!
const array = []; // this is not expression
props.expenses.forEach((item) => {
array.push(
<ExpenseItem
title={item.title}
amount={item.amount}
date={item.date}
/>
);
})
만약 forEach를 이용해서 dynamic 리스트를 만든다면 다음과 같이 작성할 수 있는데, 그냥 map을 쓰는 것이 좋다는 것을 알 수 있다.
const Expenses = (props) => {
const array = []; // 여기에서 배열을 정의한다
return (
<main className='container expenses'>
<Filter setFilterYear={setFilterYear} selectedYear={filterYear} />
{props.expenses.forEach((item) =>
array.push(
<ExpenseItem
title={item.title}
amount={item.amount}
date={item.date}
/>
)
)}
{array}
</main>
);
};
key
리액트에서 리스트 아이템을 생성했을 때, 각각의 아이템은 고유한 key를 가져야 한다는 경고 메세지가 뜨는 것을 볼 수 있다. 실제 렌더링에는 아무런 문제가 없는데, 왜 이런 경고가 뜨는 걸까? 리액트가 리스트 아이템을 렌더링하는 방식과 관련이 있다.
- 리스트를 추가하면, 그 리스트만 업데이트 되는 것이 아니라 전체 리스트가 업데이트 된다.
- 추가할 리스트가 가장 상단에 위치해야 함에도 불구하고 가장 마지막에 리스트를 추가한 뒤에, 모든 리스트의 컨텐츠를 업데이트 하는 방식을 사용하고 있기 때문이다. => low quality performance
- 고유한 identifier가 없으면, 리액트는 똑같은 구조를 가지고 있는 리스트를 구분하지 못한다.
- 단지 리스트 아이템을 담고있는 배열의 length만을 확인해서, 아이템이 추가되었을 때 리스트를 하나 더 렌더링 할 뿐이다.
- 만약 리스트 아이템이 stateful component 라면, 이 과정에서 의도치 않게 state를 업데이트하는 버그를 일으킬 수 있다.
- 컴포넌트에 global attribute인 key의 값을 설정하면 위의 문제들을 해결할 수 있다.
- 새로운 리스트 컴포넌트만 업데이트 한다.
<ExpenseItem
key={item.id}
title={item.title}
amount={item.amount}
date={item.date}
/>
'Study > React' 카테고리의 다른 글
Component Styling (0) | 2022.06.18 |
---|---|
Conditional contents (0) | 2022.06.17 |
prevState (0) | 2022.06.16 |
Event & useState (0) | 2022.06.16 |
JSX (0) | 2022.06.16 |