0. 리액트에서 state 관리를 하는 방법
setState를 통해 여러 개의 값을 상태관리하려고 할 때 온갖 방법이 있다.
하나의 변수를 관리할 때 보통 이렇게 배운다.
변수명이 userInput인 경우
const [userInput, setUserInput] = useState('')
리액트를 배우는 첫 단계의 걸음마 단계에 배우는 것이기 때문에 대부분 사람들이 다 안다.
하나의 컴포넌트에서 값을 2개 이상 관리한다고 할 때 어떻게 해야 할까?
1. 첫 번째 - useState를 여러 번 반복한다.
단순하다.
useState를 반복하는 것이다.
// 선언부
const [enteredTitle, setEnteredTitle] = useState(""); // 초기값 string
const [enteredAmount, setEnteredAmount] = useState(""); // 초기값 string
const [enteredDate, setEnteredDate] = useState("");
// 호출부
const functionName1 = (e) => {
setEnteredTitle(event.target.value);
}
const functionName2 = (e) => {
setEnteredAmount(event.target.value);
}
const functionName3 = (e) => {
setEnteredDate(event.target.value);
}
가장 심플한 방법이다.
state를 가져야 할 변수만큼 저장하는 것이다.
여러 states들을 한번에 수정하고 싶다면, 하나의 함수에 모두 넣으세요.
set에 해당하는 함수는 모두 비동기로 동작하기 때문에 각각의 기능이 모두 실행됩니다.
// 선언부
const [enteredTitle, setEnteredTitle] = useState(""); // 초기값 string
const [enteredAmount, setEnteredAmount] = useState(""); // 초기값 string
const [enteredDate, setEnteredDate] = useState("");
// 호출부
const functionName1 = (e) => {
setEnteredTitle(event.target.value);
setEnteredAmount(event.target.value);
setEnteredDate(event.target.value);
}
2. 두 번째 - 객체 형태로 삽입한다.
// 선언부
const [userInput, setUserInput] = useState({
enteredTitle: "",
enteredAmount: "",
enteredDate: "",
});
// 호출부
const titleChangeHandler = (event) => {
// setEnteredTitle(event.target.value);
setUserInput({
...userInput,
enteredTitle: event.target.value,
});
};
const amountChangeHandler = (event) => {
// setEnteredAmount(event.target.value);
setUserInput({
...userInput,
enteredAmount: event.target.value,
});
};
const dateChangeHandler = (event) => {
// setEnteredDate(event.target.value);
setUserInput({
...userInput,
enteredDate: event.target.value,
});
};
useState를 한 번만 사용하는 방법으로,
여러 state를 하나의 객체(1)의 여러 개(...)의 키와 값으로 넣는다.
그리고 여기서 중요한 것은 사용할 때 기존에 가지고 있던 객체의 값을 가져와서 넣은 후에
내가 업데이트해야 할 부분만 다시 할당한다.
기존에 가지고 있던 값을 복사해서 가지고 오기 위해 ...userInput을 사용하며 (구조분해)
거기에 내가 원하는 값만 넣는다.
예를 들어서 가장 마지막 함수의 dateChangeHandler를 보자.
이 전에 어떤 값이 있는지 모른다. (초기값은 비어있는 문자열이겠지만)
이전 값이 각자 a, a, a이었다고 하자. (enteredTitle, enteredAmount, enteredDate의 값이)
그리고 내가 원하는 enteredDate의 값을 b로 바꾸려고 한다.
이럴 때는 기존에 있던
a, a, a 객체를 가져와서 복사해서 넣고 (그대로 넣으면 안 된다. 가져와서 아예 다른 공간으로 복사)
거기에서 마지막 a를 b로 바꾼다. 여기서 b는 해당 부분의 event.target.value로 되어있다.
그러면 최종값은 a, a, b로 state가 정해진 이후에 리액트가 다시 렌더 한다.
이렇게 하는 이유는 내가 원하는 부분만 업데이트해버리면, state에 있는 다른 키의 state는 모두 잃어버리기 때문이다.
(이해가 안된 분은 댓글로 문의하시면 내용을 다시 바꿔보겠습니다 ㅠㅠ)
3. 세 번째 - 함수 형태로 작성해서 리턴한다.
위에 말한 두 번째 방식은 실제로 정상적으로 동작합니다.
하지만, 문제가 있습니다.
만약 상태가 +1씩 증가하고 있는 상태라면 이런 식으로 작성하면 안 됩니다.
새로운 폼을 만들어서 리턴해야 합니다.
이제 상태를 업데이트하는 함수를 위한 대체 폼을 만들 것입니다.
const [userInput, setUserInput] = useState({
enteredTitle: "",
enteredAmount: "",
enteredDate: "",
});
setUserInput((prevState) => {
return {...prevState, enteredTitle: event.target.value};
});
기존 state의 스냅샷을 가져와서 새로운 스냅샷을 만들어 리턴합니다.
위와 뭐가 다를까요?
리액트는 상태 업데이트 스케줄을 가지고 있어서 업데이트된 것을 바로 실행하지 않습니다.
그렇다면 업데이트가 되지 않은 상태에서 동시에 여러 상태 변경이 있을 때는 이전 방법에서는 잘못된 예전 값을 참고하고 있을 수 있습니다. 내가 원하지 않는 값에 의존할 수 있는 것이죠.
이 방식으로 하면 리액트는 항상 계획된 상태 업데이트를 염두한 상태에서, 가장 최신의 state를 가져오는 것을 보장합니다.
그래서 이전 상태에 따라 상태를 업데이트할 때 이 상수 구문을 사용하는 것이 좋습니다.
상태 업데이트에서 기존 값에 의존하고 있다면 꼭 이 함수 형태로 사용하세요.
끝.