4일차 리액트 기초(08장) - 생활코딩! React 리액트 프로그래밍
08 Create
이번 장은 CRUD 중 생성 부분을 실습해 보는 장이다. 교재 내용과 비슷하게 나도 list를 하나 만들어서 CRUD를 해볼까 한다. 아래와 같이 코드를 좀 확장해 보았다.
function List(props) {
const ltag = props.list.map((item) => (
<li key={item.id}>{item.name}</li>
));
return (
<ul>
{ltag}
</ul>
);
}
function Action(props) {
if(props.type == 'ADD') {
return (
<div>
<h2>Add</h2>
<input type='text' name='name' placeholder='name' />
<button onClick={
evt => {
const input = evt.target.parentElement.querySelector("input[name='name']");
const name = input.value;
props.callback(props.type, name);
input.value = '';
}}>OK</button>
</div>
);
}
else if(props.type == 'UPDATE') {
return (
<div>
<h2>Update</h2>
</div>
);
}
else if(props.type == 'DELETE') {
return (
<div>
<h2>Delete</h2>
</div>
);
}
}
function App() {
const [isClicked, setIsClicked] = useState(false);
const handler = ()=>{alert(`I'm a handler. more clicked: ${isClicked}`); setIsClicked(true);};
const [list, _] = useState([{id:1, name:'list1'}, {id:2, name:'list2'}, {id:3, name:'list3'}]);
const [listVer, setListVer] = useState(0);
const [actionType, setActionType] = useState('NONE');
const actionCallback = (type, ret) => {
if(type == 'ADD') {
list.push({id:list.length+1, name:ret});
setListVer(listVer+1);
}
};
return (
<div>
<Header Other='Custom' onClickHandler={handler} />
<List list={list} />
<p>
<ul style={{listStyle:'none'}}>
<li onClick={() => setActionType('ADD')}>Add</li>
<li onClick={() => setActionType('UPDATE')}>Update</li>
<li onClick={() => setActionType('DELETE')}>Delete</li>
</ul>
<Action type={actionType} callback={actionCallback}/>
</p>
</div>
);
}
간단히 설명해 보면 단순히 배열 내용을 표시하는 List라는 컴포넌트를 만들었고, List에 대한 CUD를 처리하기 위해 Action이라는 컴포넌트를 만들었다. App 컴포넌트는 has-a 관계로써 이들을 컴포지션하는 역할을 한다. 실행해 보면 아래 그림처럼 나온다.

Add를 누르면

여기에 practice1이라는 텍스트를 입력하고 OK를 누르면

교재 내용과 골자는 동일하다.
교재와 다른 점은 리스트 아이템 추가 시 리렌더링 하는 방식이다. 교재에서는 화면 갱신을 위해 아래와 같은 작업을 한다.
const [list, setList] = useState([{id:1, name:'list1'}, {id:2, name:'list2'}, {id:3, name:'list3'}]);
...
...
...
const newItem = {id:4, name:'practice1'};
const newList = [...list];
newList.push(newItem);
setList(newList);
...
...
list 객체를 deep copy하고 있다. 원소들까지 깊은 복사를 하는지 레퍼런스로 처리하는지는 모르겠으나 어쨌든 낭비적인 요소다.
그래서, 본인은 아래와 같이 list의 버전 변수를 이용하여 화면 갱신 이벤트가 발생할 수 있도록 하였다.
const [list, _] = useState([{id:1, name:'list1'}, {id:2, name:'list2'}, {id:3, name:'list3'}]);
const [listVer, setListVer] = useState(0);
...
...
const newItem = {id:4, name:'practice1'};
list.push(newItem);
setListVer(listVer+1);
...
...
교재의 코드와 다른 점이 느껴지는가? list의 크기가 커지면 커질수록 성능 차이는 명확할 것이다.
아무래도 나는 서버 개발자이다 보니 이런 부분에 민감한 것 같다.
--- 25.4.30 추가
위와 같은 버저닝 방식은 immutable 규칙을 위배한다고 한다. 왜냐하면 state로 들어간 list 객체의 내용을 직접 수정하기 때문이라고. 개인적으로는 어차피 리스트를 바꾸게 되면 리렌더링이 되는 범위가 리스트 하부 전체가 될 것이고, 브라우저의 UI thread는 싱글이기 때문에 list 자료구조의 stale 문제만 잘 케어하면 성능을 포기하면서까지 immutable 규칙을 지켜야 할까 생각이 들더라. 그런데, 조금 복잡한 실습 프로젝트를 진행하다보니 list를 state로 넣어서 나이브하게 쓸 일 자체가 없더라는...