자바스크립트 배열 객체의 내장 함수인 map
함수를 사용하여, 반복되는 컴포넌트를 렌더링할 수 있다.
map 함수는 파라미터로 전달된 함수를 사용해서 배열 내의 각 요소를 규칙에 따라 변환한 후, 그 결과로 새로운 배열을 생성할 수 있다.
arr.map(callback, [thisArg])
- callback : 새로운 배열의 요소를 생성하는 함수. 파라미터는 다음과 같다.
- currentValue : 현재 처리하고 있는 요소
- index : 현재 처리하고 있는 요소의 배열 내 index 값
- array : 현재 처리하고 있는 원본 배열
- thisArg(선택) : callback 함수 내에서 사용할 this 레퍼런스
간단하게 사용해 본 모습. 배열 내의 요소들을 각각 item으로 전달받을 수 있다.
두 번째 파라미터는 index를 갖고 있다.
export default function IterationSample() {
const names = ["눈사람", "얼음", "눈", "바람"];
const nameList = names.map(item => <li>{item}</li>);
return <ul>{nameList}</ul>;
}
names
리스트를 만들고, 각각의 요소에 대해서 <li>
태그로 감싼 리스트를 <ul
> 태그 안에 넣어서 리턴한다.
그럼 잘 나오게 되지만, 콘솔 창을 열어보면 에러가 뜬다.
모든 리스트의 자식들은 각각의 unique한 key 값을 가져야 한다고 한다.
리액트에서 key는 컴포넌트 배열을 렌더링했을 때 어떤 원소에 변화가 있었는지 알아내기 위해 사용한다.
key 값이 없다면, 리스트를 순차적으로 비교해 가면서 변화를 감지하여야 하지만, key 값이 있다면 어디서 변화가 일어났는지 빠르게 알아낼 수 있다.
key 값은 언제나 유일해야 한다. 따라서 데이터가 가진 고유한 값을 key 값으로 설정한다.
하지만 별다른 유일한 값 없이, 위의 예제처럼 ["눈사람", "얼음", "눈", "바람"]
와 같은 단순한 리스트라면 어떡할까? 앞서 살펴보았지만, 리스트 내에서 각각의 요소들이 가지는 index 값은 0, 1, 2... 이므로 unique하다고 할 수 있다. 즉 우리는 index 값을 key 값으로 사용할 수 있다.
export default function IterationSample() {
const names = ["눈사람", "얼음", "눈", "바람"];
const nameList = names.map((item, index) => <li key={index}>{item}</li>);
return <ul>{nameList}</ul>;
}
li 태그 안에 key 값으로 index를 넣어 주면 오류가 해결된다.
하지만, 고유한 값이 없을 때만 index를 사용해야 한다. 배열의 인덱스를 key 값으로 사용하면 배열이 변경될 때 효과적으로 리렌더링 하는 데에 어려움이 있기 때문이다.
아니면, 리스트를 생성할 때
[{id: 1, text: '눈사람}, {id: 2, text: '얼음'}...]
과 같이, id 값을 지정해 주어서 생성하는 방법 또한 물론 가능하다.
export default function IterationSample() {
const [names, setNames] = useState([
{ id: 1, text: "눈사람" },
{ id: 2, text: "눈" },
{ id: 3, text: "얼음" },
]);
const [inputText, setInputText] = useState("");
const [nextId, setNextId] = useState(names.length + 1);
const onChange = (e) => {
setInputText(e.target.value);
};
const onClick = () => {
setNames([...names, {id: nextId, text: inputText}]);
setNextId((curr) => curr + 1);
setInputText("");
};
const nameList = names.map((item) => <li key={item.id}>{item.text}</li>);
return (
<>
<input
type="text"
placeholder="Input something"
onChange={onChange}
value={inputText}
/>
<button onClick={onClick}>추가</button>
<ul>{nameList}</ul>
</>
);
}
이전에 투두리스트 만들 때 해봐서 쉽게 만들 수 있었다.
중요한 부분은 setNames([...names, {id: nextId, text: inputText}]);
인데 여기서 ...names
라고 해주면 기존의 names 리스트에 있던 원소들을 그대로 가져와준다. 즉 기존의 names 리스트 뒤에 새로 nextId와 inputText를 이어붙이는 기능을 한다.
그냥 names라고 하게 되면 [[(names)], (새 리스트)]
이런 식으로 서로 떨어져 버린다.
잘 추가가 되고 있다.
리스트의 각 항목을 더블클릭 했을 때, 그 항목이 화면에서 사라지는 기능을 구현해 보자.
불변성을 유지하면서, 특정 항목을 지우고자 할 때는 filter
기능을 사용한다.
filter
함수는 배열에서 특정 조건을 만족하는 원소들만 쉽게 분류할 수 있다.
3이 아닌 원소들만 리스트에 남아 있는 모습을 볼 수 있다.
const deleteList = (id) => {
setNames(names.filter((item) => item.id !== id));
};
const nameList = names.map((item) => (
<li key={item.id} onDoubleClick={() => deleteList(item.id)}>
{item.text}
</li>
));
제거하는 함수에 제거할 원소의 id값을 넘겨주고, names 리스트에서 그 id값이 아닌 원소들만 남긴 새 리스트로 setNames 해준다.
map 함수를 이용해서 반복적으로 데이터를 렌더링할 수 있다. 다만 이 과정에서 unique한 key 값을 지정해 주어야 한다.
...
와 filter
기능을 이용해서 리스트를 변형할 수 있다.