본문 바로가기

Study/React

Dynamic List

서로 같은 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 warning

 

리액트에서 리스트 아이템을 생성했을 때, 각각의 아이템은 고유한 key를 가져야 한다는 경고 메세지가 뜨는 것을 볼 수 있다. 실제 렌더링에는 아무런 문제가 없는데, 왜 이런 경고가 뜨는 걸까? 리액트가 리스트 아이템을 렌더링하는 방식과 관련이 있다. 

 

list rendering

 

  1. 리스트를 추가하면, 그 리스트만 업데이트 되는 것이 아니라 전체 리스트가 업데이트 된다. 
  2. 추가할 리스트가 가장 상단에 위치해야 함에도 불구하고 가장 마지막에 리스트를 추가한 뒤에, 모든 리스트의 컨텐츠를 업데이트 하는 방식을 사용하고 있기 때문이다. => low quality performance
    • 고유한 identifier가 없으면, 리액트는 똑같은 구조를 가지고 있는 리스트를 구분하지 못한다.
    • 단지 리스트 아이템을 담고있는 배열의 length만을 확인해서, 아이템이 추가되었을 때 리스트를 하나 더 렌더링 할 뿐이다.  
    • 만약 리스트 아이템이 stateful component 라면, 이 과정에서 의도치 않게 state를 업데이트하는 버그를 일으킬 수 있다.
  3. 컴포넌트에 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