useState로 객체를 정의해 사용해보기
import { useState } from 'react';
interface ErrorTypes {
nameError: string;
passwordError: string
}
function App () {
const [userName, setUserName] = useState('');
const [userPassword, setUserPassword] = useState('');
const [errors, setErrors] = useState<ErrorTypes>({
nameError: '',
passwordError: '',
});
const onChangeUserName = (e) => {
setUserName(e.target.value);
}
const onChangePassword = (e) => {
setUserPassword(e.target.value);
}
const handleLoginClick = () => {
if(!userName?.trim()) {
setErrors({...prev, nameError: '사용자 이름을 입력해 주세요'});
return;
}
if(!userPassword.trim()) {
setErrors({...prev, passwordError: '비밀번호를 입력해주세요'});
}
}
return (
<div>
<div>
<label htmlFor='id'>아이디</label>
<input value={userName} onChange={onChangeUserName} id='id' />
{errors.nameError && <p>{errors.nameError}</p>}
</div>
<div>
<label htmlFor='pw'>비밀번호</label>
<input value={userPassword} onChange={onChangeUserPassword} id='pw'/>
{errors.passwordError && <p>{errors.passwordError}</p>}
<div>
<button onClick={hanleLoginClick}>로그인</button>
</div>
)
}
위 코드와 같이 useState로 객체를 다룰 때, setState로 특정 값을 바꾸고 싶다면, ...prev를 입력해 기존 내용을 유지하고, 뒤에 원하는 키에 대한 값을 변경해 주는 방식을 이용하면 됩니다.
setState의 비동기적 처리와 일괄 처리(Batching)
비동기 처리
setState는 즉시 상태를 변경하지 않고, React 렌더링 사이클에 맞춰 비동기적으로 처리됩니다. 즉 여러 개의 setState의 호출이 있을 때, React에서는 이를 하나로 묶어서 일괄 처리(Batching)하여 불필요한 렌더링을 줄이고 성능을 최적화합니다.
setState(count + 1);
setState(count + 2);
setState(count + 1);
한 함수나 이벤트 핸들러 내에서 이렇게 setState가 호출된다면 +4가 더해지는 것이 아니라 이벤트가 끝날 때까지 기다렸다가 마지막으로 입력된 값을 적용합니다. 그렇다고 해서 마지막 setState만 실행되는 것은 아닙니다. 각 실행을 큐에 넣어 함수 혹은 이벤트핸들러가 끝날때까지 기다렸다가 순차적으로 실행한다고 이해하면 좋을 것 같습니다.
즉, 위와같이 호출되면 count에 4가 더해지는 것이 아니라 최종으로 실행된 setState(count + 1)이 적용되어 count에 1이 더해집니다.
함수형 업데이트
그렇다면 여러번 연속으로 상태를 변경해야 하거나 이전 상태에 의존하여 새로운 값을 계산 혹은 도출해야 한다면 어떻게 할까요? 함수형 업데이트를 사용하면 됩니다.
setState(prev => prev + 1);
처음에 객체에 값을 넣을 때에도 사용한 방식인데요. 위의 방법 뿐만 아니라
setState((prev) => {
const newPrev = [...prev];
newPrev.append("내가 원하는 값");
return newPrev
})
이런식으로 내부에서 원하는 작업을 수행한 후 그 값을 넣는 것도 가능합니다.
그렇기에 아래와 같이 한 함수 혹은 이벤트 핸들러 내에서 함수 업데이트를 이용해 여러번 호출하면 그 값이 누적되어 count에 3이 더해집니다.
setState((prev) => prev + 1);
setState((prev) => prev + 1);
setState((prev) => prev + 1);
혼합하여 여러번 호출된다면?
그렇다면 함수형 업데이트와 그냥 값을 넣는 방식이 혼합되어 사용된다면 어떻게 될까요?
처음에 이야기 한 것처럼 혼합하여 사용하는 경우에도 상태 큐 메커니즘에 따라 동작합니다. count의 초기값이 0이라 가정하고 다음 과정들을 따라가보면 이해할 수 있습니다.
setState(count + 1); // 0 + 1
setState((prev) => prev + 1); // 1 + 1 => count -> 2
setState(count + 1); // 0 + 1을 덮어씀 => count -> 1
setState((prev) => prev + 1); // 큐의 마지막 값 1 + 1
count는 2가 됩니다.
setState(count + 1); // 0 + 1
setState((prev) => prev + 1); // 1 + 1
setState((prev) => prev + 1); // 2 + 1
setState(count + 1); // 0(현재 count값을 가져옴) + 1
count는 1이됩니다. 마지막 setCount 호출을 뺀다면 3이 됩니다.
setState(count + 1); // 0 + 1
setState(count + 1); // 0 + 1 덮어씀
setState((prev) => prev + 1); // 1 + 1
setState((prev) => prev + 1); // 2 + 1
count는 3이 됩니다.
결국 마지막 값이 prev를 이용한다면 지금까지 큐에 쌓인 값을 업데이트 해주고, 그게 아니라면 초기값에 더한 값을 업데이트 해줍니다! 사실 이렇게 한 함수나 이벤트 핸들러에서 setState를 여러번 호출하는 경우는 거의 없어서 크게 신경쓰지 않아도 될 것 같습니다. 전 호기심에 이것저것 테스트 하면서 알아봤습니다 ㅋㅋ
그리고 저렇게 setState를 여러번 호출하는 과정에서 중간에 count를 찍어도 일괄처리 되어 반영되는 것이기 때문에 반영되기 전 count 값이 찍힙니다.
'React 19' 카테고리의 다른 글
[React] React 19를 알아보자! (5) Custom Hook (0) | 2025.05.09 |
---|---|
[React] React 19를 알아보자! (4) useRef와 클로저 문제 & ref 콜백 클린업 함수 (0) | 2025.05.08 |
[React] React 19를 알아보자! (2) 프로젝트 setting & useState의 파생 상태와 지연 초기화 (0) | 2025.05.07 |
[React] React 19를 알아보자! (1) React의 핵심 개념 (2) | 2025.05.07 |