치춘짱베리굿나이스
prevState 사용 이유 본문
prevState 사용 이유
const [testValue, setTestValue] = useState(false);
...
setTestValue(!testValue);
우선 아무 생각 없이 현재 상태값을 그대로 가져와서 이렇게 작성하던 과거의 나를 반성하자
쓰는 법
const [testValue, setTestValue] = useState(false);
...
setTestValue((prevState) => !prevState);
간단하다
setState()
함수 내에 인라인 함수를 작성하면 된다
인자로 prevState
가 자동으로 들어간다
근데 왜 굳이 써요?
const [testValue, setTestValue] = useState(0);
const handleOnClick = () => {
setTestValue(testValue + 1);
setTestValue(testValue + 1);
setTestValue(testValue + 1);
setTestValue(testValue + 1);
setTestValue(testValue + 1);
};
...
<div>{`${testValue}`}</div>
<div onClick={handleOnClick}>add 1</div>
앞의 경우는 상태값이 true
, false
두 가지 경우밖에 없기 때문에 와닿지 않을 수 있다
한 번의 클릭으로 상태값이 5번 바뀐다고 정의해보자
클릭할 때마다 값이 5씩 증가할 것 같은 비주얼이다
하지만 우리의 예상은 보기좋게 빗나갔다
마치 5씩 증가할것 같다고 예상한 우리들을 비웃듯 한 번의 클릭당 값이 1씩만 증가하는 것을 볼 수 있다
왜 그런 건가요
함수에는 setTestValue
가 순서대로 5줄 적혀있기 때문에 슬쩍 보기엔 5개의 setTestValue
가 순서대로 실행될 것만 같다. 하지만 setState
는 비동기로 작동한다, 그 말인 즉슨 5개의 setTestValue
가 서로를 기다려주지 않는다는 것
따라서 testValue
가 각각의 setTestValue
에서 바뀌었더라도 다른 setTestValue
는 그것을 모르며, 단지 리액트에게 “현재 testValue
에서 값을 1 더해서 저장해 줘" 라는 요청만을 할 뿐이고, 리액트는 이 요청들을 모아 값을 한번에 갱신한다
한줄요약: setState는 상태값을 바꾸라는 요청을 할 뿐이지 실제로 상태값을 바꿔주진 않으며, 값을 바꿔주는건 리액트가 알아서 한다
⇒ 마지막에 요청되는 변경 사항으로 요청이 오버라이딩 된다
요청이 오버라이딩 된다구여?
const [testValue, setTestValue] = useState(0);
const handleOnClick = () => {
setTestValue(testValue + 1);
setTestValue(testValue + 1);
setTestValue(testValue + 1);
setTestValue(testValue + 1);
setTestValue(testValue + 3);
};
...
<div>{`${testValue}`}</div>
<div onClick={handleOnClick}>add 1</div>
마지막에 요청되는 변경 사항으로 오버라이딩 된다 라는 게 무엇인지 위의 예시를 보자
위의 4개의 setTestValue
는 기존의 testValue
에서 1을 더해주고, 나머지 1개의 setTestValue
는 기존의 testValue
에서 3을 더해준다
결과값이 어떻게 될까
값이 맨 마지막에 정의된 대로 3씩 증가한다
const [testValue, setTestValue] = useState(0);
const handleOnClick = () => {
setTestValue(testValue + 1);
setTestValue(testValue + 1);
setTestValue(testValue + 3);
setTestValue(testValue + 1);
setTestValue(testValue + 1);
};
...
<div>{`${testValue}`}</div>
<div onClick={handleOnClick}>add 1</div>
이렇게 3을 더해주는 요청이 중간에 위치한다면 어떻게 될까?
다시 1씩 증가하는 것을 볼 수 있다
setTestValue
5개가 각각 비동기적으로 (병렬적으로) 처리되긴 하지만, 마지막으로 들어온 요청으로 덮어씌워지는구나! 라고 생각하면 쉬울 것 같다
그래서 prevState는 뭔데요?
위와 같이 비동기적으로 setState가 수행되는 것 때문에, 직전 state값을 가지고 작업을 하려고 하면 원하는 대로 값이 잘 변하지 않을 때가 있다
이때 prevState를 사용하면 나름 동기적으로 작업을 수행할 수 있다
const [testValue, setTestValue] = useState(0);
const handleOnClick = () => {
setTestValue(prevState => prevState + 1);
setTestValue(prevState => prevState + 1);
setTestValue(prevState => prevState + 1);
setTestValue(prevState => prevState + 1);
setTestValue(prevState => prevState + 1);
};
...
<div>{`${testValue}`}</div>
<div onClick={handleOnClick}>add 1</div>
앞의 코드를 prevState
를 이용하여 바꿔보자
setTestValue
안에 인자를 받는 함수를 사용하면 되고, 인자에는 prevState
값이 자동으로 들어가므로 이름은 어떻게 해도 상관없지만 기왕 이쁘게 하자
드디어 우리가 예상했던 대로 값이 5씩 증가한다
setState
가 리액트 측에 요청을 보낼 때, 앞에서는 “testValue
에 1을 더해주세요" 라고 요청을 보냈지만, 이번에는 “이전 상태값에 1을 더해주세요" 라는 요청을 보냈기 때문에 오버라이딩 되지 않고 요청된 순서대로 값이 바뀌었다
정말 순서대로 요청이 들어가고 있는지 확인해보자
const [testValue, setTestValue] = useState('');
const handleOnClick = () => {
setTestValue(prevState => prevState + '1');
setTestValue(prevState => prevState + '2');
setTestValue(prevState => prevState + '3');
setTestValue(prevState => prevState + '4');
setTestValue(prevState => prevState + '5');
};
...
<div>{`${testValue}`}</div>
<div onClick={handleOnClick}>add 1</div>
숫자 연산으로 하면 순서가 헷갈리니 문자열 연산으로 바꾸어 보았다
순서대로 요청이 들어간다면 문자열은 “12345”
가 되어야 할 것
예상하던 대로 값이 잘 처리되었다
매우 편안하다...
결론
prevState
는 비동기적으로 작동하던 setState
를 나름 동기적으로 동작하게 도와주므로, 상태값이 기대하던 대로 잘 변하도록 보장해줄 수 있다
다르게 말하자면 prevState
를 사용하지 않으면 내가 예상하던 대로 값이 제대로 바뀌지 않을 수 있다
이와 비슷한 문법으로 async
, await
가 있으며 얘네는 상태값 관련이 아니더라도 사용하여 값의 변화를 기다리고 동기적으로 작업을 수행할 수 있다
참고자료
비동기로 동작하는 react의 setState에 대하여
'ClientSide > React' 카테고리의 다른 글
데이터 불러오기, Suspense (0) | 2022.05.13 |
---|---|
IntersectionObserver + 무한스크롤 (0) | 2022.05.12 |
Custom Hook (0) | 2022.05.09 |
Too many re-renders 오류 (0) | 2022.05.07 |
CSS Module (0) | 2022.05.04 |