컴포넌트를 구축하는 방법과 여러 컴포넌트와 통신하는 방법, state 사용하는 방법, 사용자 이벤트를 다루는 방법을 배웠다.
Rendering List, Conditional Content를 렌더링하는 방법에 대해 기록할 것이다. 페이지에서 어떻게 데이터의 배열을 출력할 수 있는지와 다양한 조건에서 다양한 컨텐츠를 보여줄 수 있는 방법을 알아보자.
동일한 컴포넌트들을 나열하는 경우에 얼마나 반복될 지 모르는 경우에 사용한다. 하드코딩을 지양해야한다. 목록들을 동적으로 렌더링해보자.
JSX 코드에서 동적 구문을 사용하기 위해 중괄호가 필요하다.
Vue 3에서의 보간법(Interpolation)은 중괄호 2개를{{ }} 사용한다. React에서는 중괄호 1개를{ } 사용한다.
중괄호 안에서 JS 내장함수 .map을 사용한다.
const 컴포넌트함수 = (props) => {
return (
{props.items.map(item => (
<자식컴포넌트
amount={item.amount}
title={item.title}
/>
))}
);
}
하지만 이 때 아래와 같은 에러가 발생한다.
Warning: Each child in a list should have a unique "key" prop.
const 컴포넌트함수 = (props) => {
return (
{props.items.map(item => (
<자식컴포넌트
amount={item.amount}
title={item.title}
key={item.id} // 고유값 부여
/>
))}
);
}
리액트의 key 속성은 데이터의 목록을 렌더링하는 데 발생할 수 있는 어떠한 성능 손해나 버그없이 효과적으로 목록들을 변경하고 렌더링할 수 있도록 보장하기 위해 존재한다.
key 없이 데이터를 추가하여 리스트에 다른 목록이 추가되는 것을 DevTools > Elements에서 살펴보자. 배열의 가장 첫 번째 인덱스에 데이터가 추가되었으나, DOM에서는 목록들 전체가 깜빡이는 모습을 볼 수 있다.
key없이 새로운 아이템을 추가하면 DOM에서 가장 마지막으로 렌더링하고 모든 아이템들을 업데이트하여 목록들의 하위 컨텐츠들을 모두 교체한다. 성능상 안좋음.
그리고 stateful 컴포넌트의 경우 내부에 들어있는 state 배열의 0번째 인덱스의 값도 모두 뒤엎게 되어 사이드이펙트를 발생할 수 있다.
새로운 아이템이 어디에 추가되어야 하는지 리액트에게 알려주기 위해 key 속성을 사용해주어야 한다.
const 컴포넌트함수 = (props) => {
return (
{props.items.map((item, idx) => (
<자식컴포넌트
amount={item.amount}
title={item.title}
key={item.idx} // 인덱스값 부여
/>
))}
);
}
.map()의 idx를 활용하는 방법도 존재한다. 하지만 특정 아이템에 대한 인덱스가 항상 똑같으며, 아이템 컨텐츠에 직접적으로 추가된 값이 아니기 때문에 버그를 발생시킬 수 있다. DB에도 고유 식별자가 존재한다.
key 속성을 추가하면 DOM 요소가 더이상 깜빡거리지 않는다. 하위 컨텐츠도 대체가 되는 것이 아니라 목록 내 원하는 위치에 아이템이 정상적으로 추가되는 것을 볼 수 있다.
목록에 아이템을 매핑할 때는 항상 key를 추가해야한다.
이 부분은 Vue와 동일하다.
// 필터링 컴포넌트
<select
value={props.selected}
onChange={변경핸들러함수}
>
<option value="all">모두</option>
<option value="2021">2021년</option>
<option value="2022">2022년</option>
</select>
// 목록 컴포넌트
const filteredItems = props.item
.filter(item => {
if (filteredYear === 'all) return item;
return item.date.getFullYear() === +filterYear;
});
<div>
{filteredItems
.map(item => (
<아이템컴포넌트
key={item.id}
title={item.title}
date={item.date}
/>
))
}
</div>
props.item 배열을 JS 내장함수 .filter()를 사용하여 필터링된 새로운 배열을 출력하는 변수를 만들어 JSX 코드에서 렌더링한다.
조건부 컨텐츠는 각각 다른 상황에서 다양한 출력 값을 렌더링하는 것이다.
만일 특정 조건에서의 목록이 빈 배열(없는 결과)이 나오는 경우에 데이터가 없음을 알리는 메시지를 표현할 때 렌더링하고 싶은 경우에 필요하다.
JSX 코드 내 중괄호 안에 연산자를 사용하여 표현할 수 있다.
삼항 연산자나 이항 연산자의 경우 Vue에서 v-if, v-else-if, v-else 기능과 동일하다.
const editHandler = isEdit => () => {
setIsEdit(isEdit);
};
return (
<div>
{!isEdit ? (
<button onClick={editHandler(true)}>Add Item</button>
) : (
<ItemForm
onSaveItemData={saveItemHandler}
onCancel={editHandler(false)}
/>
)}
</div>
);
JSX 요소에 바인딩하는 이벤트 핸들러 함수는 무조건 콜백 함수 타입이어야 한다. Vue3의 경우 template에 리스너로 추가하는 함수의 경우 소괄호를 쓰든 안쓰든 무조건 소괄호가 있다는 식으로 생각하면 된다. 그래서 아래와 같은 형태로 바인딩할 수 있고, 개발자는 그 부분에 대해 신경을 안써도 되는 편리함을 가지는 것 같다.
이에 비해 JSX에서는 바인딩하는 함수가 콜백함수여야 하며, 중괄호 안에 파라미터를 추가하려면 고차 함수(Higher-Order Function)을 사용해야하는 것 같다.<template> <button v-if="isEdit" onClick="editHandler(true)" Add Item </button> <ItemForm v-else onSaveItemData="saveItemHandler" // 소괄호가 없는 모습 onCancel="editHandler(false)" // 소괄호가 있음(파라미터와 함께) </ItemForm> </template> <script setup lang="ts"> const isEdit = ref<boolean>(false); const editHandler = (isEdit: boolean): void => { isEdit.value = isEdit; } const saveItemHandler = (): void => { // ... } </script>
html에 style 요소는 존재한다. react에서는 아래와 같이 사용해야한다.
<div className="chart-bar">
<div
className="chart-bar__fill"
style={{ height: 높이설정된반응형변수, 'background-color': 'black' }}
>
</div>
</div>