리액트에서 반복되는 데이터를 렌더링하는 방법을 알아보자. key와 불변성 유지 개념도 배우게 될 것이다.
const IterationSample = () => {
return (
<ul>
<li>A</li>
<li>B</li>
<li>C</li>
<li>D</li>
</ul>
);
};
export default IterationSample;
html에서 코드를 작성하다보면 같은 형태를 여러번 반복해야 되는 때가 생긴다. jsx에서 이런 코드를 작성할 때는 비교적 단순하게 반복되는 부분을 처리할 수 있다. 일례로 배열을 사용하는 방법이 있다.
const IterationSample = () => {
const names = ['A','B','C','D'];
const nameList = names.map(name=><li>{name}</li>);
return <ul>{nameList}</ul>;
};
export default IterationSample;
map을 통해 기존 리스트의 요소를 각각 리스트 태그를 감싼 새로운 배열을 반환받은 후 이를 렌더링했다. 렌더링 결과는 맨 처음 작성했던 코드와 같다.
물론 위 코드를 렌더링 하는 데 자체는 문제가 없었지만 콘솔창을 확인해보면 아래와 같은 오류가 떠있다. key prop이 뭘까 , 왜 필요한걸까?
[사진]
key는 컴포넌트 내 배열에서 어떤 원소에 변동이 있었는지 알아내기 위해 사용된다. key가 없다면 가상 DOM과 비교하는 과정에서 순차적으로 하나하나 비교해야 하지만 , key가 있다면 어떤 변화가 일어났는지 더 빠르게 추적 가능하다.
예제 코드에도 map함수의 내장 index를 사용해 key를 설정해보자.
const IterationSample = () => {
const names = ['A','B','C','D'];
const nameList = names.map((name,index)=><li key={index}>{name}</li>);
return <ul>{nameList}</ul>;
};
export default IterationSample;
위처럼 key를 설정하면 개발자 도구에서 오류를 출력하지 않는다. 다만 알아두어야 할 부분은 예시를 위해 index를 key값으로 사용한 것이지 보통은 바람직하지 않다.
지금까지 배운 내용들을 종합해 input으로 입력받은 값을 배열에 추가하고, 더블클릭으로 배열값을 삭제하는 기능을 구현해 볼 것이다.
import { 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 nameList = names.map(name=><li key={name.id}>{name.text}</li>);
return <ul>{nameList}</ul>;
}
export default IterationSample;
기본적인 틀은 이렇다. 이제 여기에 기능들을 붙여보자.
import { 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 onChange = (e) => setInputText(e.target.value);
const onClick = () =>{
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 (
<div>
<input value={inputText} onChange={onChange} />
<button onClick = {onClick}>추가</button>
<ul>{nameList}</ul>
</div>
);
};
export default IterationSample;
[렌더링된 사진]
input에 입력한 값을 배열에 배열에 추가해 렌더링하고 있다. 배열 객체의 id값을 key로 사용 중이다. 기본적으로 다 배운 내용이기에 특별한 건 없지만 하나 주목해야 할 부분이 있다
const onClick = () =>{
const nextNames = names.concat({
id: nextId,
text : inputText
});
setNextId(nextId + 1);
setNames(nextNames);
setInputText('');
}
배열을 업데이트하는 부분을 보면, push로 객체 하나를 붙여주는 식이 아닌
앞서 객체나 배열의 state를 일부 수정할 때는 사본을 만들어 덮어씌우는 방식을 사용하라고 배웠다.
리액트에서 상태를 업데이트 할 때는 기존 상태를 그대로 두면서 새로운 값을 상태로 설정해야 한다. 이를 불변성 유지라고 한다.
불변성 유지를 해주어야 리액트 컴포넌트 성능을 최적화할 수 있다고 하는데 , 관련 내용은 후반부에 알아보도록 하자.
import { 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 onChange = (e) => setInputText(e.target.value);
const onClick = () =>{
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 (
<div>
<input value={inputText} onChange={onChange} />
<button onClick = {onClick}>추가</button>
<ul>{nameList}</ul>
</div>
);
};
export default IterationSample;
완성된 코드다. onDoubleClick
이 처음 사용되었다. <li>
요소 각각에 더블클릭 이벤트를 할당해 삭제 기능을 구현했다. 배열을 업데이트할 때 역시나 불변성 유지를 위해 filter
를 사용해 덮어씌우는 방식을 사용했다. 렌더링해보면 잘 작동한다.
[사진]
컴포넌트 반복보다는 불변성 유지 / key에 무게가 많이 실렸던 장이다. 불변성 유지에 대한 정확한 설명이 나오지 않았기에 아직 완전한 이해는 불가하지만 여기서 이걸 더 파고들면 딱히 남는 것도 없고 진도는 진도대로 멈추는 걸 알기에.. 일단은 그렇구나~ 마인드로 넘어가자. 앞으로 마치며도 꼭 쓰면서 내용 간단히 정리할 것....