state ➡️ view,view ➡️ Event Handler ➡️ setState() ➡️ stateView의 변경이 직접 State를 변경시키지 않고 Event Handler를 통해서만 변경 가능하게 구현해야 하므로, 상태가 변경되는 과정에 대한 예측과 추적이 용이하다.브라우저 DOM에 바로 적용하지 않고 브라우저 DOM과 유사한 트리 구조의 가상 DOM을 수정한 후 수정 전의 가상 DOM과 수정 후의 가상 DOM을 비교해서 바뀐 부분만 브라우저 DOM에 실제 반영DOM API를 이용한 화면 갱신 방법React.createElement() 직접 사용)❌ 여러 객체 반환할 수 없음
return (
<h1>Todo List</h1>
<div>...<div>
);
⭕️ 루트 요소를 추가하여 단일 객체 반환
return (
<div>
<h1>Todo List</h1>
<div>...<div>
</div>
);
⭕️ Fragment 사용 - 렌더링에 영향 미치지 않음 <> </>로 약어 가능
return (
<Fragment>
<h1>Todo List</h1>
<div>...<div>
</Fragment>
);
ex) onclick -> onClick onkeyup -> onKeyUp
<div id="todolist" class="todo"></div>
document.querySelector('#todolist').className = 'todo'
<div id="todolist" className="todo"></div>
const todoClass = 'todo'
<div id="todolist" className={ todoClass }></div>
{} 안에는 변수 값, 메서드 리턴 값 등 값만 사용 가능if for 등은 사용할 수 없다.if ➡️ 삼항 연산자 (item.done ? <s>두부</s> : '두부')
for ➡️ forEach() , map(){ // ❌ for문
for(let i=0; i<itemList.length; i++){
return item.title;
}
}
// ⭕️ map
{ itemList.map(item => item.title) }
{ } 내부의 값이 HTML 코드가 포함된 문자열인 경우 HTML 태그를 인코딩해서 처리하므로 브라우저에는 태그가 그대로 보여짐const App(){
const msg = '<i>World</i>';
return <span>Hello { msg }</span>
}
만들어지는 문자열: <span>Hello <i>World</i></span>
예시 결과: Hello <i>World</i>
✔️ 해결 방법 (dangerouslySetInnerHTML) - HTML 태그 인코딩 X
const App(){
// { msg }를 <span dangerouslySetInnerHTML=></span>로 변경
const msg = '<i>World</i>';
return <span>Hello <span dangerouslySetInnerHTML=></span></span>
}
✔️ JSX는 XSS 공격에 안전하므로 JSX 사용
const App(){
// const msg = '<i>World</i>';
const msg = <i>World</i>;
return <span>Hello { msg }</span>
}
function App(){
const title = 'React Props';
let list = [
{ _id: 1, title: '리그오브 레전드', done: false},
{ _id: 2, title: '영화 보기(집에서)', done: false},
{ _id: 3, title: '던파', done: false},
];
return (
<div id="app">
<div>
<Title title={ title } />
<TodoList list={ list } />
</div>
</div>
);
}
function Title({ title='Default Title' }){
return (
<div>
<h1>Simple Todo List - { title } :()</h1>
<hr />
</div>
);
}
function TodoList({ list }){ // 구조 분해 할당
const itemList = list.map(item => {
return (
<li key={ item._id }>{ item.title }</li>
);
});
return (
<ul className="todolist">
{ itemList }
</ul>
);
}
함수에 데이터를 전달할 때, 인수를 사용하듯이 컴포넌트에 데이터를 전달할 때 사용
하위 컴포넌트에는 상위 컴포넌트가 전달한 여러 속성이 하나의 Props 객체로 전달되므로, 구조 분해 할당을 이용해서 필요한 속성 사용
기본값 매개변수를 사용하면 Props가 전달되지 않거나 undefined가 명시적으로 전달될 때 적용
Props로 객체를 전달 받을 때 자식 컴포넌트가 그 값을 직접 변경하는 것은 지양
자신이 전달받은 Props 전체를 하위 컴포넌트에 전달하고 싶을 때는 전개 연산자 사용
function Profile(props) {
return (
<div>
<Avatar { ...props } />
</div>
);
}
const [state, setState] = useState(initialState);
initialState - 상태의 초기값 (초기 렌더링 후 무시)state - 저장된 상태값setState - 상태값을 변경하는 setter 함수setter를 통해 상태가 변경되면 해당 컴포넌트는 다시 렌더링 됨{} 내부에서 사용불가)➡️ 원본이 바뀌지 않고 새로운 배열이 나오는 메서드를 사용해야 한다.
| 피해야 할 메서드 | 추천하는 메서드 | |
|---|---|---|
| 추가 | push(), unshift() | concat(), [...arr] |
| 삭제 | pop(), shift() | filter(), slice() |
| 수정 | splice(), arr[i] | map() |
| 정렬 | reverse(), sort() 바로 사용x | 배열 복사 후 사용 |
중첩 객체일 경우에는 불변성을 위해 수정될 속성을 포함한 객체와 그 객체를 포함하는 객체를 루트 객체까지 거슬러 올라가면서 전부 교체해야할 수 있다.
맨 끝단의 수정되는 객체가 있으면 윗단의, 윗단의, 윗단으로 거슬러 올라가 모든 부모를 교체해주어야 한다.
객체를 통째로 복제하면 시간과 비용이 너무 많이 들기 때문에 참조형인 것이다. (주소만 복사)
따라서 복합 객체를 수정하려면 맨 위의 부모만 바꾸는 것이 아니라 끝 단까지 가기 위한 부모를 모두 복제해야 한다.
✔️ 상태의 불변성 유지
const newAddressBook = user.extra.addressBook.map(address => {
if(address.id === Number(e.target.name)){
return { ...address, value: e.target.value };
}else{
return address;
}
});
const newState = {
...user,
extra: {
...user.extra,
addressBook: newAddressBook
}
};
setUser(newState);
이 때 각각의 부모 노드들을 전부 복제하는 것은 복잡한 작업이기 때문에 객체를 불변성으로 만들어주는 라이브러리가 있다.
npm install inner
✔️ immer 사용한 상태의 불변성 유지
const newState = produce(user, draft => { // user를 복사한 draft
const address = draft.extra.addressBook.find(address => address.id === Number(e.target.name));
address.value = e.target.value;
});
setUser(newState);
부모를 쫓아다니면서 복제한 객체들을 produce로 반환해준다.
교체가 필요한 부분만 복제를 해서 사용하고, 아닌 부분은 그대로 예전의 주소값을 참조한다.