React에서 상태(state
)를 직접 변경하지 않고 setState
를 사용하는 이유는 두 가지 주요한 이유가 있습니다:
상태 변경의 비동기성: React는 성능 최적화를 위해 여러 setState
호출을 한 번에 처리하는 비동기 업데이트를 수행합니다. 이러한 업데이트 방식 때문에 상태 변경을 즉시 반영하지 않을 수 있습니다. 따라서 상태를 직접 변경하면 예상하지 못한 결과가 발생할 수 있습니다.
재렌더링의 트리거링: setState
는 컴포넌트에게 상태가 변경되었음을 알리고, 그 결과 컴포넌트의 재렌더링을 트리거합니다. 상태를 직접 변경하면 React는 이 변경사항을 인지하지 못하므로 컴포넌트가 자동으로 재렌더링되지 않습니다.
React의 useState
훅은 상태를 관리하며 이전 상태에 기반한 업데이트를 안전하게 수행할 수 있도록 설계되어 있습니다. useState
훅은 상태 변경 함수를 제공하며, 이 함수는 이전 상태를 인수로 받는 콜백 함수를 통해 상태를 업데이트합니다.
그러나 useState
의 상태 업데이트 함수는 클래스 컴포넌트의 setState
와는 달리 항상 동기적으로 동작합니다. 즉, 상태 변경 함수가 호출되면 상태가 즉시 업데이트됩니다. 그리고 상태가 변경되면 해당 컴포넌트는 재렌더링을 트리거합니다.
하지만 여러 번의 상태 업데이트가 같은 이벤트 핸들러 내에서 일어나면, React는 이를 하나의 단일 업데이트로 배치하고 한 번에 처리합니다. 이는 불필요한 재렌더링을 방지하고 성능을 향상시키는데 도움이 됩니다.
예를 들어, 아래와 같이 상태 업데이트 함수를 여러 번 호출하더라도 상태는 한 번에 업데이트됩니다:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={increment}>
Click me
</button>
</div>
);
}
위의 코드에서, increment
함수는 setCount
를 세 번 호출하지만, 상태(count
)는 1만큼만 증가합니다. 이는 setCount
호출이 모두 같은 count
값에 기반하기 때문입니다.
이러한 문제를 해결하기 위해, setCount
는 이전 상태를 인수로 받는 콜백 함수를 제공합니다. 이 함수를 사용하면, 상태 업데이트가 순차적으로 실행되어 상태가 올바르게 업데이트됩니다.
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={increment}>
Click me
</button>
</div>
);
}
위의 코드에서는 각 setCount
호출이 이전 상태(prevCount
)에 기반하여 상태를 업데이트하므로, 상태(count
)는 3만큼 증가하게 됩니다. 이러한 방식으로 useState
훅을 사용하면 상태 변경의 비동기성을 올바르게 관리할 수 있습니다.