상태를 관리할 수 있게 해주는 훅
✔️ useState 사용 X > 동작하지 X는 코드1 : 일반 변수 사용
그냥 메모리에서만 바뀌고, React는 그걸 모르기 때문에 화면은 그대로 'hello'를 보여주게 된다. 리렌더링 발생 조건 충족 X
function Component() {
let state = 'hello'
function handleButtonClick() {
state = 'hi'
}
return (
<>
<h1>{state}</h1>
<button onClick={handleButtonClick}>hi</button>
</>
)
}
✔️ useState 사용 X > 동작하지 X는 코드2 : 리액트에서 렌더링이 일어나게 변경
매번 렌더링이 발생될 때마다 함수는 다시 새롭게 실행 > 새롭게 실행되는 함수에서 state는 매번 'hello'로 초기화된다.
function Component() {
const [, triggerRender] = useState()
let state = 'hello'
function handleButtonClick() {
state = 'hi'
triggerRender()
}
return (
<>
<h1>{state}</h1>
<button onClick={handleButtonClick}>hi</button>
</>
)
}
✔️ useState 구조
클로저클로저 : 어떤 함수
useState내부에 선언된 함수setState가 함수의 실행이 종료된 이후에도useState가 호출된 이후에도지역변수인 state를 계속 참조할 수 있다는 것
(실제 리액트 코드에서는 useReducer를 이용해 구현)
// [코드 3.16] useState 내부의 모습을 구현한 모습
// MyReact라고 불리는 클로저 내부에 useState와 관련된 정보를 저장해 두고,
// 필요할 때마다 꺼내놓는 형식으로 구성
const MyReact = (function () {
const global = {} // 전역 객체에 모든 상태 저장 (모든 useState 값들을 순서대로 저장)
let index = 0 // 현재 처리 중인 useState의 순서
function useState(initialState) {
if (!global.states) {
// 애플리케이션 전체의 states 배열을 초기화한다.
// 최초 접근이라면 빈 배열로 초기화한다.
global.states = []
}
// states 정보를 조회해서 현재 상태값이 있는지 확인하고,
// 없다면 초깃값으로 설정한다.
const currentState = global.states[index] || initialState
// states의 값을 위에서 조회한 현재 값으로 업데이트한다.
global.states[index] = currentState
// 즉시 실행 함수로 setter를 만든다.
const setState = (function () {
// 현재 index를 클로저로 가둬놔서 이후에도 계속해서 동일한 index에
// 접근할 수 있도록 한다.
let currentIndex = index // 현재 index를 클로저로 캡처
return function (value) {
global.states[currentIndex] = value // 항상 같은 위치에 저장
// 컴포넌트를 렌더링한다. 실제로 컴포넌트를 렌더링하는 코드는 생략했다.
}
})()
// useState를 쓸 때마다 index를 하나씩 추가한다. 이 index는 setState에서 사용된다.
// 즉, 하나의 state마다 index가 할당돼 있어 그 index가 배열의 값(global.states)을
// 가리키고 필요할 때마다 그 값을 가져오게 한다.
index = index + 1
return [currentState, setState]
}
// 실제 useState를 사용하는 컴포넌트
function Component() {
const [value, setValue] = useState(0)
// ...
}
return { useState, Component }
})()
- React는 호출 순서로 상태를 구분
컴포넌트가 리렌더링될 때마다 같은 순서로 useState 호출
첫 번째 useState는 항상 states[0], 두 번째는 항상 states[1]❓ 컴포넌트 여러 개에서 useState를 쓰는데, 이때 모두 global에 순서대로 저장되어 있는걸까 ?
React는 컴포넌트별로 독립적인 훅 배열을 관리한다.
✔️ 게으른 초기화
useState의 인수로 원시값이 아닌, 특정한 값을 넘기는 함수를 인수로 넣어주는 것
// ❌ 매번 함수 실행 (값은 첫번째만 사용)
const [state, setState] = useState(expensiveFunction())
// ✅ 처음에만 함수 실행
const [state, setState] = useState(() => expensiveFunction())