엘리먼트 모음을 만들고 중괄호 {}를 이용해서 JSX에 포함시킬 수 있다.
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
ReactDom.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);
일반적으로 컴포넌트 안에서 리스트를 랜더링한다.
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
이 코드를 실행하면 리스트의 각 항목에 key 를 넣어야 한다는 경고가 표시된다.
"key" 는 엘리먼트 리스트를 만들 때 포함해야 하는 특수한 문자열 어트리뷰트이다.
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
key 는 React 가 어떤 항목을 변경, 추가 또는 삭제할지 식별하는 것을 돕는다.
key는 엘리먼트에 안정성인 고유성을 부여하기 위해 배열 내부의 엘리먼트에 지정해야한다.
const numbers = [1, 2, 3, 4, 5]
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
key 를 선택하는 가장 좋은 방법은 리스트의 다른 항목들 사이에서 해당 항목을 고유하게 식별할 수 있는 문자열을 사용하는 것이다. 대부분 데이터의 ID를 key로 사용한다.
const todoItems = todo.map((todo) =>
<li key={todo.id}}>
{todo.text}
</li>
)
렌더링 한 항목에 안정적인 아이디가 없다면 최후의 수단으로 항목의 index 를 사용할 수 있다.
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
항목의 순서가 바뀔 수 있는 경우 key 인덱스를 사용하는 것을 권장하지 않는다.
성능이 저하되거나 컴포넌트의 state와 관련된 문제가 발생할 수 있다.
리스트 항목에 명시적으로 key 를 지정하지 않으면 React 는 기본적으로 인덱스를 key 로 사용한다.
DOM 노드의 자식들을 재귀적으로 처리할 때 리액트는 기본적으로 동시에 두 리스트를 순회하고 차이점이 있으면 변경을 생성한다.
자식 끝에 엘리먼트를 추가하면 두 트리 사이의 변경은 잘 작동한다.
<ul>
<li>first</li>
<li>second</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
React 는 두 트리에서
<li>first</li>
가 일치하는 것을 확인하고 이어서
<li>second</li>
가 일치하는것을 확인한다.
그리고 마지막으로
<li>third</li>
를 트리에 추가한다.
하지만 위와 같이 단순하게 구현하면, 리스트의 맨 앞에 엘리먼트를 추가하는 경우 성능이 좋지 않다. 예를 들어, 아래의 두 트리 변환은 형편없이 작동한다.
<ul>
<li>Duke</li>
<li>Villanova</li>
</ul>
<ul>
<li>Connecticut</li>
<li>Duke</li>
<li>Villanova</li>
</ul>
이러한 문제를 해결하기 위해 React 는 key 속성을 지원하고 자식들이 키를 가지고 있다면 키를 통해 기존 트리와 이후 트리의 자식들이 일치하는 것을 확인한다.
<ul>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
<ul>
<li key="2014">Connecticut</li>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
이제 React는 '2014' key를 가진 엘리먼트가 새로 추가되었고, '2015'와 '2016' key를 가진 엘리먼트는 그저 이동만 하면 되는 것을 알 수 있다.
key는 반드시 변하지 않고, 예상 가능하며, 유일해야 합니다. 변하는 key(Math.random()으로 생성된 값 등)를 사용하면 많은 컴포넌트 인스턴스와 DOM 노드를 불필요하게 재생성하여 성능이 나빠지거나 자식 컴포넌트의 state가 유실될 수 있다.
키는 주변 배열의 context 에서만 의미가 있다.
예를 들어 ListItem 컴포넌트를 추출한 경우 ListIte 안에 있는 li 엘리 먼트가 아니라 배열의 <ListItem / > 엘리먼트가 키를 가져야 한다.
function ListItem(props) {
// 여기에는 key를 지정할 필요가 없다.
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// 배열 안에 key를 지정해야 한다.
<ListItem key={number.toString()} value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
React에서 key는 힌트를 제공하지만 컴포넌트로 전달하지는 않는다. 컴포넌트에서 key와 동일한 값이 필요하면 다른 이름의 prop으로 명시적으로 전달한다.
const content = posts.map((post) =>
<Post
key={post.id}
id={post.id}
title={post.title} />
);
위에 예시에서 Post 컴포넌트는 props.id 는 읽을 수 있지만 props.key는 읽을 수 없다.
별도의 listItems 변수를 선언하고 이를 JSX 에 포함한다.
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) => {
<ListItem key={numbers.string()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
JSX를 사용하면 중괄호 안에 모든 표현식을 포함 시킬 수 있으므로 map() 함수의 결과를 인라인 으로 처리할 수 있다.
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={numbers.toSring()}
value={number} />
)}
</ul>
)
}
이 방식은 코드가 더 깔끔해 지지만 남발하는 것은 좋지 않다. 가독성을 위해 변수로 추출할지 인라인으로 넣을지는 개발자가 판단해야 한다. map() 함수가 너무 중첩 된다면 컴포넌트로 추출하는 것이좋다.