
const InteratinoSample = () => {
return (
<ul>
<li>눈사람</li>
<li>얼음</li>
<li>눈</li>
<li>바람</li>
</ul>
);
};
다음 코드를 보면
가 반복되는 형태를 볼 수 있습니다.지금은 그렇게 많지는 않지만 프로젝트 규모가 커지고 코드가 증가한다면, 불필요한 용량이 증가하고, 데이터가 유동적이라면 관리하기에 힘들어집니다.
따라서 이번 6장에서는 반복적인 내용을 효율적으로 보여 주고 관리하는 방법을 알아보겠습니다.
자바스크립트 배열 객체의 내장 함수인 map함수를 사용하여 반복되는 컴포넌트를 렌덛링할 수 있습니다. map 함수는 파라미터로 전달된 함수를 사용해서 배열 내 각 요소를 원하는 규칙에 변환한 후 새로운 배열을 생성합니다.
arr.map(callback , [thisArg])
맵 함수의 파라미터는 다음과 같습니다.
자 이제 컴포넌트에서 사용해 보겠습니다.
const InteratinoSample = () => {
const names = ["눈사람", "얼음", "눈", "바람"];
const nameList = names.map((name) => <li>{name}</li>);
return <ul>{nameList}</ul>;
};
export default InteratinoSample;
map() 함수에서 JSX를 작성할 때는 앞서 다룬 예제처럼 DOM 요소를 작성해도 되고, 컴포넌트를 사용해도 됩니다.
그러나 저렇게 함수를 만들고 화면에 렌더링 시 콘솔창을 열어보면 경고 메세지가 나타납니다.
“key” prop이 없다는 경고 메세지를 표시합니다. key란 무엇일까요?
리액트에서 key는 컴포넌트 배열을 렌더링했을 때 어떤 원소에 변동이 있었는지 알아내려고 사용합니다. 예를 들어 유동적인 데이터를 다룰 때는 원소를 새로 생성할 수도, 제거할 수도, 수정할 수도 있죠.
key가 없을 때는 Virtual DOM을 비교하는 과정에서 리스트를 순차적으로 비교하면서 변화를 감지합니다.
하지만 key가 있다면 이 값을 사용하여 어떤 변화가 일어났지는 더욱 빠르게 알아낼 수 있습니다.
key 값을 설정할 때는 map 함수의 인자로 전달되는 함수 내부에서 컴포넌트 props를 설정하듯이 설정하면 됩니다. key 값은 언제나 유일해야하고, 데이터가 가진 고윳값을 key로 해야합니다.
예를 들어 게시판의 게시물을 렌더링 한다면 게시물 번호를 key 값으로 설정하면 됩니다.
이제 앞서 만든 예제에 key값을 넣어 봅시다.
const InteratinoSample = () => {
const names = ["눈사람", "얼음", "눈", "바람"];
const nameList = names.map((name, index) => <li key={index}>{name}</li>);
return <ul>{nameList}</ul>;
};
export default InteratinoSample;
이제 더이상 경고메세지가 나타나지 않습니다.
이제 고정 배열을 렌더링 하는 것이 아닌, 동적인 배열을 렌더링하는 것을 구현해봅시다.
그리고 index값을 key로 사용하면 리렌더링이 비효울적이라고 배웠는데, 이러한 상황에 어떻게 고윳값을 만들 수 있는지도 알아보겠습니다.
이제 useState를 사용하여 객체 향태로 이루어진 배열을 만들어 봅시다.
해당 객체에는 문자열과 고유 id 값이 있습니다.
import { useState } from "react";
const InteratinoSample = () => {
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 nameList = names.map((name) => <li key={name.id}>{name.text}</li>);
return <ul>{nameList}</ul>;
};
export default InteratinoSample;
map() 함수를 사용할 때 key 값을 index 대신 name.id 값으로 지정하였습니다.
새로운 이름을 등록할 수 있는 기능을 구현해 봅시다.
input 과 button을 렌더링하고, input 상태를 관리해봅시다.
다음 onClick 함수에서 내장 함수 concat을 사용하여 새로운 항목을 추가한 배열을 만들고, setNames를 통해 상태를 업데이트 해줍니다.
import { useState } from "react";
const InteratinoSample = () => {
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 onChange = (e) => setInputText(e.target.value);
const onClcik = () => {
const nextNames = names.concat({
id: nextId,
text: inputText,
});
setNextId(nextId + 1);
setNames(nextNames);
setInputText("");
};
const nameList = names.map((name) => <li key={name.id}>{name.text}</li>);
return (
<>
<input value={inputText} onChange={onChange} />
<button onClick={onClcik}>추가</button>
<ui>{nameList}</ui>
</>
);
};
export default InteratinoSample;
현재 배열에 새 항목을 추가할 때 배열의 push 함수를 사용하지 않고 concat을 사용했는데요, push 함수는 기존 배열 자체를 변경하는 반면, concat은 새로운 배열을 만들어 준다는 차이점이 있습니다.
리액트에서 상태를 업데이트 할 때는 기존 상태를 그대로 두면서 새로운 값을 상태로 설정해야 합니다.
이를 불변성 유지라고 하는데요. 불변성 유지를 해 주어야 나중에 리액트 컴포넌트의 성능을 최적화 할 수 있습니다.
새 항목을 만들었으니 다음은 데이터를 제거하는 기능을 구현해 봅시다.
각 항목을 더블클릭 했을 때 해당 항목이 화면에서 사라지는 기능을 구현해 보겠습니다.
생성과 마찬가지로 불변성을 유지하기 하면서 업데이트 해주기 위해서는 배열의 내장 함수인 filter를 사용합니다.
filter 함수를 사용해 봅시다.
onDoubleClick 과 onRemove 로 제거를 만들어 봅시다.
import { useState } from "react";
const InteratinoSample = () => {
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 onChange = (e) => setInputText(e.target.value);
const onClcik = () => {
const nextNames = names.concat({
id: nextId,
text: inputText,
});
setNextId(nextId + 1);
setNames(nextNames);
setInputText("");
};
const onRemove = (id) => {
const nextNames = names.filter((name) => name.id !== id);
setNames(nextNames);
};
const nameList = names.map((name) => (
<li key={name.id} onDoubleClick={() => onRemove(name.id)}>
{name.text}
</li>
));
return (
<>
<input value={inputText} onChange={onChange} />
<button onClick={onClcik}>추가</button>
<ui>{nameList}</ui>
</>
);
};
export default InteratinoSample;