2021-06-29
6장 컴포넌트 반복
웹 어플리케이션을 만들다 보면 반복되는 코드를 작성할 때가 있다. 동적으로 요소를 생성하고 반복되는 내용을 효율적으로 보여주고 관리하는 방법을 알아보도록 한다.
6.1 자바스크립트 배열의 map() 함수
자바스크립트 배열 객체의 내장 함수인 map 함수를 사용하여 반복되는 컴포넌트를 렌더링할 수 있다. map 함수는 파라미터로 전달된 ㅎ마수를 사용해서 배열 내 각 요소를 원하는 규칙에 따라 변환한 후 그 결과로 새로운 배열을 생성한다.
map 함수의 설명
/**
* Calls a defined callback function on each element of an array, and returns an array that contains the results.
* @param callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
**/
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
map 함수의 정의는 위와 같다. callbackfn 콜백함수 파라미터는 value, index(number), array 세 가지 값을 가지고 thisArg는 선택 항목이며 callback 함수 내부에서 사용할 this 레퍼런스이다.
간단한 예시
const numbers = [1, 2, 3, 4, 5];
const result = numbers.map((num) => num * num);
console.log(result);
결과 : 1, 4, 9, 16, 25
callbackfn 에 num * num 이 들어간 것이고 그에 대한 파라미터로 value : num 을 받은 것이다.
6.2 데이터 배열을 컴포넌트 배열로 변환하기
import React from "react";
const IteratioSample = () => {
const names = ["눈사람", "얼음", "눈", "바람"];
const nameList = names.map((name) => <li>{name}</li>);
return <ul>{nameList}</ul>;
};
export default IterationSample;
Warning : Each child in a list should have a unique “key” prop.
위와 같은 경고 메시지가 출력된다.
6.3 key
리액트에서 key는 컴포넌트 배열을 렌더링 했을 때 어떤 원소에 변동이 있었는지 알아내려고 사용한다. key가 없을 때는 Virtual DOM을 비교하는 과정에서 리스트를 순차적으로 비교하면서 변화를 감지한다. 하지만 key가 있으면 이 값을 사용해서 어떤 변화가 일어났는지 빠르게 알 수 있다.
key 값을 설정할 때는 map 함수의 인자로 전달되는 함수 내부에서 컴포넌트 props를 설정하듯이 설정하면 된다. key의 값은 고유해야 한다.
6.4 응용
IterationSample.js
import React, { useState } from "react";
const IterationSample = () => {
const [names, setNames] = useState([
{ id: 1, text: "눈사람" },
{ id: 2, text: "얼음" },
{ id: 3, text: "눈" },
{ id: 4, text: "바람" },
]);
const [inputText, setInputText] = useState("");
const [nextId, setNextId] = useState(5);
// const namesList = names.map((name) => <li key={name.id}>{name.text}</li>);
const onChange = (e) => setInputText(e.target.value);
const onClick = () => {
// nextNames 라는 새로운 배열 생성
const nextNames = names.concat({
id: nextId,
text: inputText,
});
// nextId의 상태를 +1 한 상태로 새롭게 업데이트 시킨다.
setNextId(nextId + 1);
// 생성된 새로운 배열을 names 배열로 상태를 업데이트 시킨다.
setNames(nextNames);
// inputText의 값을 "" 빈 문자열로 상태를 업데이트 시킨다.
setInputText("");
};
const onRemove = (id) => {
const nextNames = names.filter((name) => name.id !== id);
setNames(nextNames);
};
const namesList = names.map((name) => (
<li ket={name.id} onDoubleClick={() => onRemove(name.id)}>
{name.text}
</li>
));
return (
<>
<input value={inputText} onChange={onChange} />
<button onClick={onClick}>추가</button>
<ul>{namesList}</ul>
</>
);
};
export default IterationSample;
리액트에서 상태를 업데이트할 때는 기존 상태를 그대로 두면서 새로운 값을 상태로 설정해야 한다. 이를 불변성 유지 라고 하는데 불변셩 유지를 해 주어야 나중에 리액트 컴포넌트의 성능을 최적화 할 수 있다.
데이터 제거 기능은 filter를 통해서 구현했다. filter는 불변성을 유지하면서 배열의 특정 항목을 지울 때 사용하며 배열의 내장 함수이다.
filter 함수의 설명
/**
* Returns the elements of an array that meet the condition specified in a callback function.
* @param predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.
* @param thisArg An object to which the this keyword can refer in the predicate function. If thisArg is omitted, undefined is used as the this value.
**/
filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[];
6.5 정리
반복되는 데이터를 렌더링하는 방법에 대해서 배웠는데, 이는 배열을의 내장 함수들인 map과 filter의 사용법이다. 컴포넌트 배열을 렌더링 할 때는 반드시 key 값 설정에 대해서 주의 해야 한다. key 값은 언제나 고유해야하고 상태 안에서 배열을 변경할 때는 직접 접근해서 수정하는 것이 아니라 concat, filter 등의 배열 내장 함수를 사용하여 새로운 배열을 만든 후 이를 새로운 상태로 설정해 주어야 한다.