state 변수를 설정하면 큐에 다음 렌더링이 들어간다고 했음.
근데, 다음 렌더링을 큐에 넣기 전에 그 값을 가지고 다른 수행도 하고 싶을 때가 있을거 같은데,,,,
이 시점을 어떻게 조절할 수 있을까 ?
React state batches 업데이트
이 전 장에서 한 번에 3씩 증가하는게 아닌 1씩 증가하는 것으로 알 수 있었다.
state의 값은 그 시점의 값으로 고정되어 있기 때문이라 했는데, 한 가지 더 알아둬야 할 점이 있다.
React는 state 업데이트 하기 전 이벤트 핸들러의 모든 코드가 실행될 때까지 기다린다.
어떤 손님이 "양념 치킨 시킬게요. 아 아니다 간장으로 주세요. 아 근데 좀 비싸네 그냥 후라이드로 하나 주세요"
라고 한다면, 웨이터는 주방을 왔다갔다하며 "양념이요 ~ => 아 아니다 간장으로 바꿔주세요 ~ => 결국은 후라이드네요 ~" 하는게 아니고 걍 바로 "후라이드 하나요" 하는거임
심지어 "후라이드 하나"를 기억했다가 다른 테이블의 주문도 받을 수 있음
이렇게 하면 리렌더링이 많이 일어나는걸 방지할 수 있음
그리고 다수의 주문도 처리가 가능함. 즉, 다수의 state 변수 업데이트도 가능하다.
단 !!!
이러면, 모든 주문을 받기 전까지 요리가 시작되지 않는다는 것과 같다.
코드가 완료되기 전까지 UI가 업데이트 안된다는 뜻
batching을 사용하면, React 앱을 빠르게 실행할 수 있게 해줌 !
다음 렌더링 전에 동일한 state 변수를 여러 번 업데이트 하기
해당 시점의 고정된 state를 사용하지 않고, 렌더링 전에 여러 번 업데이트 하려면 !?
단순히 state 값을 대체하는게 아닌 "state 값으로 무언가를 하라"고 지시하는 방법으로 가능하다.
함수를 전달한다는 뜻
setNumber(number + 1) => setNumber(number => number + 1)
이제 3씩 올라감을 확인 가능함.
여기서 n => n + 1 을 업데이터 함수(updater function)라고 함
React가 이벤트 핸들러의 모든 코드를 실행한 후 해당 함수를 큐에 넣음.
그리고 순차적으로 업데이터 함수를 수행하고, 최종적인 state를 제공하여 렌더링이 큐에 들어가게 되는거임
queued update (큐에 쌓인 함수) | n | returns |
n => n + 1 | 0 | 0 + 1 = 1 |
n => n + 1 | 1 | 1 + 1 = 2 |
n => n + 1 | 2 | 2 + 1 = 3 |
그래서 +3을 클릭하면 3씩 올바르게 증가하게 되는거임
state를 교체한 후 업데이트하면 어떻게 되나요 ?
<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
}}>
그렇다면 위의 코드는 어떻게 될까 ?
아까 말했듯이 최종 업데이트된 state가 제공된다고 했음
그래서 위의 버튼을 클릭하면 6씩 증가함
queued update | n | returns |
"replaced with 5" | 0 (unused) | 5 |
n => n + 1 | 5 | 5 + 1 = 6 |
setState(5)가 결국엔 setState(n => 5)처럼 동작하는건데 n이 사용안되는거임.
생략된거지
업데이트 후 state를 바꾸면 어떻게 될까 ?
아래의 코드는 어떻게 될까 ?
<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
setNumber(42);
}}>
queued update (큐에 쌓인 함수) | n | returns |
"replace with 5" | 0 (unused) | 5 |
n => n + 1 | 5 | 5 + 1 = 6 |
"replace with 42" | 6 | 42 |
좀 헷갈림
// number는 렌더링 시점의 스냅샷
setNumber(n => number + 1);
// n은 연산 시점의 값
setNumber(n => n + 1);
// 걍 상수
setNumber(n => 42);
// 위의 로직은 동기적으로 실행된다.
로 이해했음.
근데 우얐든 헷갈리니, 업데이터 함수는 순수해야 사이드 이펙트를 줄일 수 있음
React Strict 모드는 각 업데이터 함수를 두 번 실행하여 순수 함수인지 아닌지를 찾아주기 때문에 개발에 도움이 됨
명명 규칙
업데이터 함수 인수의 이름은 해당 state 변수의 첫 글자로 지정하는 것이 일반적 !
setEnabled(e => !e);
setLastName(ln => ln.reverse());
setFriendCount(fc => fc * 2);
좀 더 자세한 코드를 원하면 (내 스타일)
setEnabled(enabled => !enabled)
setEnabled(prevEnabled => !prevEnabled)
이렇게도 가능
근데 나는 setEnabled(prevEnabeld => !prevEnabled) 이게 좋당
문제가 있는 코드는 클릭 시점의 state를 가지고 연산을 하는게 문제임
setPending(pending + 1); // 이 때 0이고
await delay(3000);
setPending(pending - 1); // 3초 뒤에 연산에서도 0으로 해버림
setCompleted(completed + 1);
그래서, 연산 시점의 값을 가지고 제어하고 싶으면 업데이터 함수를 넘기면 됨
그러면 클릭 당시가 아닌 최신 state를 가지고 카운터를 줄이거나 늘리는게 가능
setState가 어떻게 동작하는지 이해할 수 있게되는 문제인데, 좋은 문제인거 같다.
export function getFinalState(baseState, queue) {
let finalState = baseState;
queue.map(q => {
if (typeof q === 'number') {
finalState = q;
} else {
finalState = q(finalState);
}
})
return finalState;
}
난 이렇게 품
'Web + APP > React' 카테고리의 다른 글
[리액트 스터디] - Reacting to Input with State (0) | 2024.07.27 |
---|---|
[리액트 스터디] - Updating Arrays in State (0) | 2024.07.26 |
[리액트 스터디] - State as a Snapshot (2) | 2024.07.18 |
Flux 패턴 (0) | 2021.03.19 |
React 소개 with 이벤트 위임 (4) | 2021.03.16 |