React: useState와 클로저
React에서 상태 업데이트는 동적인 부분을 다루는 핵심적인 부분으로 컴포넌트에서 데이터 상태를 관리할 수 있게 만들어 주는 기능이다.
상태를 업데이트 할 때 아래와 같이 2가지 방법을 사용해서 업데이트 할 수 있다.
-
setState내에 변경할 값을 넣는 방법
-
setState에 함수를 넣는 방법
상태를 업데이트 할 때, 1번보다 2번으로 업데이트하는 것을 권장하고 있는거 같다.
위의 두가지 방법에 어떤 차이가 있는지 알아보자!!
먼저 아래 예시로 살펴보자.
- setState내에 변경할 값을 넣는 방법
function App() {
const [count, setCount] = useState(0)
const handleCount = () => {
setCount(count + 1)
}
return (
<div>
<button onClick={handleCount}>버튼</button>
<span>{count}회 클릭하였습니다.</span>
</div>
)
}
- setState에 함수를 넣는 방법
function App() {
const [count, setCount] = useState(0)
const handleCount = () => {
setCount((prev) => prev + 1)
}
return (
<div>
<button onClick={handleCount}>버튼</button>
<span>{count}회 클릭하였습니다.</span>
</div>
)
}
이렇게 handleCount함수 안에서 상태 업데이트를 한번씩 해주면 정상적으로 동작 하는 것 처럼 보일 것이다!
하지만 아래와같이 하나의 함수안에서 여러번의 상태를 업데이트 해보자..
- setState내에 변경할 값을 넣는 방법
function App() {
const [count, setCount] = useState(0)
const handleCount = () => {
setCount(count + 1)
setCount(count + 1)
setCount(count + 1)
}
return (
<div>
<button onClick={handleCount}>버튼</button>
<span>{count}회 클릭하였습니다.</span>
</div>
)
}
- setState에 함수를 넣는 방법
function App() {
const [count, setCount] = useState(0)
const handleCount = () => {
setCount((prev) => prev + 1)
setCount((prev) => prev + 1)
setCount((prev) => prev + 1)
}
return (
<div>
<button onClick={handleCount}>버튼</button>
<span>{count}회 클릭하였습니다.</span>
</div>
)
}
첫번째 방법 경우 setCount(count + 1)가 여러 번 호출되더라도 리액트는 이를 한번의 업데이트로 처리하기 때문에 count는 1만큼 증가하게 될 것이다.
두번째 방법의 경우 setCount(prev => prev + 1)은 함수를 사용하여 이전 상태 값을 받아와 업데이트를 수행하는데
이 때, React는 이 함수를 통해 이전 상태 값을 정확하게 가져오기 때문에 여러 호출이 있더라도 한 번의 업데이트로 처리하기 때문에 count는 3만큼 증가하게 된다.
useState는 클로저를 통해 상태를 관리하고 있다.
클로저는 함수가 선언될 당시의 환경을 기억하는데, 위의 두 번째 방법에서 사용된 함수는 prev 변수를 통해 이전 상태 값을 받아오기 때문에 클로저의 특성을 활용하고 있는데, 이렇게 클로저를 통해 함수가 선언된 시점의 prev 값을 유지하며 업데이트를 하고 있는 것이다.
아래와 같은 방법으로도 생각해보자.
- setState내에 변경할 값을 넣는 방법
import React, { useState } from 'react'
function App() {
const [count, setCount] = useState(0)
const handleCount = () => {
setTimeout(() => {
setCount(count + 1) // 클로저 문제가 발생할 수 있는 코드
}, 1000)
}
return (
<div className='App'>
<span>{count}회 클릭하였습니다.</span>
<button onClick={handleCount}>버튼</button>
</div>
)
}
export default App
- setState에 함수를 넣는 방법
import React, { useState } from 'react'
function App() {
const [count, setCount] = useState(0)
const handleCount = () => {
setTimeout(() => {
setCount((current) => current + 1) // 함수형 업데이트 사용
}, 1000)
}
return (
<div className='App'>
<span>{count}회 클릭하였습니다.</span>
<button onClick={handleCount}>버튼</button>
</div>
)
}
export default App
첫 번째 방법에서 setCount(count + 1)을 사용할 경우, 클로저 문제가 발생할 수 있다.
setCount 함수는 비동기적으로 동작하고, 클로저는 해당 함수가 호출될 때의 환경을 기억하고 있을 것이다.
그래서 count 값이 항상 업데이트된 최신 값을 반영하지 못하는 문제가 발생할 수 있을 것이다.
이러한 이유로 React에서는 상태를 업데이트할 때는 setState에 함수를 사용하는 것을 권장하고 있으며, 함수를 사용하면 React가 제공하는 최신 상태를 정확하게 가져와 업데이트할 수 있다.
이렇게 useState와 클로저를 함께 고려해서 작성하게 된다면, 비동기적인 상황에서도 안정적인 상태 값을 업데이트를 할 수 있게 될 것이다.