컴포넌트에는 생명주기가 있다.
생명주기란?
화면상에 떠오르고(mount) <- render 를 통해 이루어진다
화면상에 갱신되고(update) <- render 를 통해 이루어진다
화면상에서 죽는다(unmout)
의 과정들이 있다.
불변성
불변성 : 메모리에 있는 값을 변경할 수 없는 것
원시데이터: 숫자, 문자, 불리언
원시데이터가 아닌 것들: 배열, 객체, 함수 데이터할당 과정이 다르다
데이터할당 과정 / 데이터를 수정하는 경우
**분홍색 줄은 call stack 영역
원시데이터의 데이터 할당과정 ↓
원시데이터 데이터를 수정하는 경우 ↓
VS 원시데이터 (숫자,불리언...)
값이 변경되면 새로운 데이터 블록을 만들어 참조한다. 불변성이 있다.
**분홍색 줄은 call stack 영역
**초록색 줄은 Memory Heap 영역
원시데이터가 아닌 것들의 데이터할당과정 ↓
분석해보기 (더보기 클릭)
let obj1 = {
name: 'kim',
}
let obj2 = {
name: 'kim',
}
obj1 과 obj2 값이 같은가? 네
obj1 과 obj2는 같은가? = 같은 데이터 주소값을 바라보고 있는가? 아니오
console.log(obj1 === obj2);
// false
값은 같지만, 실제로 일치하진 않는다.
원시데이터가 아닌 것들의 데이터를 수정하는 경우 ↓
분석해보기 (더보기 클릭)
let obj1 = {
name: 'kim',
}
obj1.name = "park" // 데이터 변경 // 객체는 불변성이 없다
let obj2 = {
name: 'kim',
}
값이 변경되었어도 여전히 같은 데이터 주소값을 바라보고 있다.
왜? 데이터 주소값이 바라보고 있는 데이터 공간에 있는 데이터를 바꾸기 때문에 불변성이 없다!
데이터 구조와 처리과정
리액트와 불변성
component가 화면에 나오기
조건 : rendering
조건: state가 변경되는 것
- 리액트는 화면을 렌더링할지를 state의 변화에 따라 결정한다.
- 단순 변수는 무시한다.
왜 리액트에서는 원시데이터가 아닌 데이터의 불변성을 지켜주는 것을 중요시할까?
원시데이터가 아닌것들(배열, 객체 등)에서 데이터를 변경 할 때, 주소값이 바뀌는 것이 아니라 그 주소값이 바라보고 있는 데이터 영역의 값이 바뀌기때문에 리액트는 그것을 '메모리 값'이 변경되었다고 인식하지 않는다. 이떄문에 화면에 데이터변경값이 렌더링되지 않는 것!
결국, 원시데이터가 아닌 것들의 데이터 수정방식과 리액트의 특성떄문에
우리는 원시데이터가 아닌 데이터의 불변성을 지켜줘야 한다. 그래야 화면에 데이터변경값이 렌더링 되기 때문!
결국 피해야하는 방법 : mutable(불변성이 없어요!)
원본 배열의 데이터를 수정하는 approach 는 피해야한다.
const obj.name = "원래값";
////
obj.name = "변경값"; // 원본 배열 데이터를 직접 바꾼 셈
setObj({...obj}); // {name: "변경값"} // -> 작동은 되지만, 불변성이 보장되지 않았다.
Then..HOW? 순수함수 사용하기!
순수함수: 하나 이상의 인자를 받고, 인자를 변경하지 않고, 참조하여 새로운 값을 반환하는 함수 즉, 같은 input동일한 인자가 전달되면 항상 동일한 결과를 반환하는 함수 Same Input, Same Output, No Side Effect!
각 컴포넌트는 순수함수로 만들어야 한다.
리액트에서 순수함수 쓰기
- 컴포넌트의 많은 루틴을 순수 함수로서 작성하기를 요구하고 있다.
- 컴포넌트에서 state와 props가 같으면, 항상 같은 값을 반환해야한다
- 다른 Side effects를 발생시키지 않아야 한다. (HTTP 요청, 데이터 저장, 쿠키 조작 등)
- 컴포넌트의 상탯값은 불변 객체(Immutable Object)로 관리해야 한다.
- 수정할 때에는 기존 값을 변경하는 것이 아니라, 같은 이름의 새로운 객체를 생성한다
- 이를 통해, UI 개발의 복잡도를 낮추고, 버그 발생 확률도 줄인다.
- 같은 입력에 대해 항상 같은 출력을 보장하니, 테스트 하기도 훨씬 수월하다
어떤 방법으로 순수함수를 만들까?
(1) 새로운 객체 만들어 순수함수 만들기
새로운 객체를 만들어 원본 객체를 복사한다. 그러면 완전히 분리된 데이터주소값을 가지게 된다. 그 이후에, 데이터를 변경해주면 된다.
const obj.name = "원래값";
////
const obj2 = {...obj, name: "변경값" };
setObj(obj2); // -> 화면 출력됨 // { name : "변경값" }
혹시.....
spreadOperator 를 사용하는데, 만약 depth가 깊은 중첩 객체를 상태로 가질 경우에는 불변성의 여부를 점검하기 어려워지기 때문에, mutable한 문법으로도 불변성을 지킬 수 있는 Immer 라이브러리 사용을 권장한다.
기존 spreadOperator 사용시
const [obj, setObj] = useState({
name: "James",
contact: {
email: "abc@gmail.com",
phone: "010-1234-5678"
}
})
const changeEmailTo = (newEmail) => {
setObj({ ...obj, contact: {...obj.contact, email: newEmail} })
}
immer 라이브러리에서 produce hook을 가져와서 썼을 때
import { produce } from "immer";
const [obj, setObj] = useState({
name: "James",
contact: {
email: "abc@gmail.com",
phone: "010-1234-5678"
}
})
const changeEmailTo = (newEmail) => {
setObj(
produce(Obj2 => {
Obj2.contact.email = newEmail;
})
)
}
'부트캠프 개발일지 2023-2024 > React 리액트' 카테고리의 다른 글
[5주차] 리액트입문: 카운터 앱 만들기 (0) | 2023.11.02 |
---|---|
[5주차] 리액트입문: component, rendering, browser rendering (1) | 2023.11.02 |
[5주차] 리액트입문: State (0) | 2023.11.01 |
[5주차] 리액트입문: props, props children, prop drilling, props 추출 (1) | 2023.10.31 |
[5주차] 리액트입문: JSX 문법 (1) | 2023.10.31 |