map() 메서드는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환한다.
const array1 = [1, 4, 9, 16];
const map1 = array1.map((x) => x * 2);
console.log(map1); // [ 2, 8, 18, 32 ]
arr.map(callback(currentValue[, index[, array]])[, thisArg])
매개변수 callback
새로운 배열 요소를 생성하는 함수. 다음 세 가지 인수를 가진다.
1. currentValue: 처리할 현재 요소
2. index: 처리할 현재 요소의 인덱스
3. array: map()을 호출한 배열
thisArg: callback을 실행할 때 this로 사용되는 값
반환 값
배열의 각 요소에 대해 실행한 callback의 결과를 모은 새로운 배열
map()을 사용하기 위해 배열을 하나 만들자.
import React from "react";
const vegetables = ["감자", "고구마", "오이", "가지", "옥수수"];
return (
<div className="app-style">
{vegetables.map((vegetableName) => {
return (
<div className="square-style" key={vegetableName}>
{vegetableName}
</div>
);
})}
</div>
);
};
export default App;
JSX 부분에서 map() 즉, 자바스크립트 코드를 작성할 것이기 때문에 { } 로 먼저 감싸고 시작한다.
JSX에서 map() 은 배열의 모든 요소를 순회한다. 그래서 클라이언트에서는 배열 형태의 데이터를 활용해서 화면을 그려주는 경우가 많고, 이때 배열의 값들로 동적으로 컴포넌트를 만들 수 있다.
map을 사용하니 중복된 코드가 사라지고 1개의 컴포넌트를 이용하면서 그 안에서 <div>{vegetableName}</div> 가 순차적으로 보여지고 있다.
import React from "react";
import "./App.css";
// User 컴포넌트를 분리해서 구현
function User({user}) {
return (
<div>{user.age}살 - {user.name}</div>
);
}
function App() {
// const vegetables = ["감자", "고구마", "오이", "가지", "옥수수"];
const users = [
{ id: 1, age: 30, name: "송중기" },
{ id: 2, age: 24, name: "송강" },
{ id: 3, age: 21, name: "김유정" },
{ id: 4, age: 29, name: "구교환" }
];
return (
<div className="app-container">
{users.map((user) => {
return <User user={user} key={user.id} />;
})}
</div>
);
};
export default App;
User 컴포넌트에 props로 들어오는 user 정보인 {user}는 { id: 1, age: 30, name: "송중기" } 라는 정보가 들어온다.
이처럼 우리는 map()의 기능을 이용해서 반복되는 컴포넌트를 간단하게 화면에 표시할 수 있다.
이제 유저를 추가하고, 삭제하는 연습을 해보자
import React, { useState } from "react";
import "./App.css";
const User = ({ user }) => {
return (
<div>{user.age}살 - {user.name}</div>
);
}
function App() {
// useState를 이용한 상태값 만들기
const [userList, setUserList] = useState([
{ id: 1, age: 30, name: "송중기" },
{ id: 2, age: 24, name: "송강" },
{ id: 3, age: 21, name: "김유정" },
{ id: 4, age: 29, name: "구교환" }
]);
// 유저의 입력값을 담을 상태
const [name, setName] = useState('');
const [age, setAge] = useState('');
const addUSerHandler = () => {
const newUser = {
id: userList.length + 1,
age: age,
name: name
};
setUserList([...userList, newUser]);
};
return (
<div className="app-container">
<input value={name} placeholder="이름을 입력해주세요"
onChange={(e) => setName(e.target.value)} />
<input value={age} placeholder="나이를 입력해주세요"
onChange={(e) => setAge(e.target.value)} />
{userList.map((user) => {
return <User user={user} key={user.id} />;
})}
<button onClick={addUSerHandler}>추가하기</button>
</div>
);
};
export default App;
input 박스와 유저의 입력값 담을 상태를 선언해주면 유저 추가 끝!
이제 유저 삭제를 해보자.
import React, { useState } from "react";
import "./App.css";
const User = (props) => {
return (
<div className="user-card">
<div>{props.user.age}살 - {props.user.name}</div>
<button onClick={() => props.handleDelete(props.user.id)}>
삭제
</button>
</div>
);
}
function App() {
// useState를 이용한 상태값 만들기
const [userList, setUserList] = useState([
{ id: 1, age: 30, name: "송중기" },
{ id: 2, age: 24, name: "송강" },
{ id: 3, age: 21, name: "김유정" },
{ id: 4, age: 29, name: "구교환" }
]);
// 유저의 입력값을 담을 상태
const [name, setName] = useState('');
const [age, setAge] = useState('');
const addUSerHandler = () => {
const newUser = {
id: userList.length + 1,
age: age,
name: name
};
setUserList([...userList, newUser]);
};
const deleteUserHandler = (id) => {
const newUserList = userList.filter(user => user.id !== id);
setUserList(newUserList);
};
return (
<div className="app-container">
<input value={name} placeholder="이름을 입력해주세요"
onChange={(e) => setName(e.target.value)} />
<input value={age} placeholder="나이를 입력해주세요"
onChange={(e) => setAge(e.target.value)} />
{userList.map((user) => {
return <User user={user} key={user.id}
handleDelete={deleteUserHandler} />;
})}
<button onClick={addUSerHandler}>추가</button>
</div>
);
};
export default App;
컴포넌트 분리
버튼들이 많아졌으므로 계속 재사용하기 위해 버튼 컴포넌트로 분리해보자.
아래와 같이 컴포넌트를 따로 분리해주면, 추가하기 버튼은 초록색, 삭제하기 버튼은 빨강색으로 더 간편하게 표현할 수 있다.
import React, { useState } from "react";
import "./App.css";
// 버튼 컴포넌트 분리 (분리 시 CSS하기 더 편함)
function CustomButton(props) {
const { color, onClick, children } = props
if (color)
return (
<button
style={{ background: color, color: "white" }}
onClick={onClick}>
{children}
</button>
);
return <button onClick={onclick}>{props.children}</button>;
}
// User 컴포넌트 분리
const User = (props) => {
return (
<div className="user-card">
<div>{props.user.age}살 - {props.user.name}</div>
{/* 버튼 컴포넌트로 바꾸기 */}
<CustomButton color="red" onClick={() => props.handleDelete(props.user.id)}>
삭제
</CustomButton>
</div>
);
}
function App() {
// useState를 이용한 상태값 만들기
const [userList, setUserList] = useState([
{ id: 1, age: 30, name: "송중기" },
{ id: 2, age: 24, name: "송강" },
{ id: 3, age: 21, name: "김유정" },
{ id: 4, age: 29, name: "구교환" }
]);
// 유저의 입력값을 담을 상태
const [name, setName] = useState('');
const [age, setAge] = useState('');
const addUserHandler = () => {
const newUser = {
id: userList.length + 1,
age: age,
name: name
};
setUserList([...userList, newUser]);
};
const deleteUserHandler = (id) => {
const newUserList = userList.filter(user => user.id !== id);
setUserList(newUserList);
};
return (
<div className="app-container">
<input value={name} placeholder="이름을 입력해주세요"
onChange={(e) => setName(e.target.value)} />
<input value={age} placeholder="나이를 입력해주세요"
onChange={(e) => setAge(e.target.value)} />
{userList.map((user) => {
return <User user={user} key={user.id}
handleDelete={deleteUserHandler} />;
})}
{/* 버튼 컴포넌트로 바꾸기 */}
<CustomButton color="green" onClick={addUserHandler}>추가</CustomButton>
</div>
);
};
export default App;
리액트에서 map을 사용하여 컴포넌트를 반복 렌더링 할 때는 반드시 컴포넌트에 key를 넣어줘야 한다. 넣지 않으면 오류가 발생할 수 있다.
key가 필요한 이유는 React에서 컴포넌트 배열을 렌더링했을 때 각각의 원소에서 변동이 있는지 알아내려고 사용하기 때문이다. 만약 key가 없다면 React는 가상돔을 비교하는 과정에서 배열을 순차적으로 비교하면서 변화를 감지하려고 한다. 하지만 key가 있으면 이 값을 이용해서 어떤 변화가 일어났는지 더 빠르게 알아낼 수 있게 된다.
즉, key값을 넣어줘야 React의 성능이 더 최적화된다.
map() 안에서 조건식을 사용할 수 있다.
age가 25 미만인 user는 User 컴포넌트를 이용해 화면에 렌더링 해주고, 그것이 아닐 때는 화면에 아무것도 렌더링 하지 않기 위해 null 을 return한다.
두 피연산자의 값이 서로 같으면 true를 반환한다.
이때 두 피연산자의 타입이 서로 다르면, 비교를 위해 강제로 타입을 같게 변환한다.
타입의 변환 없이 두 피연산자의 값이 같고, 타입도 같아야만 true를 반환한다.