0. setState 함수
- useState 에서 setState 함수는 상태값을 변경하여 컴포넌트를 리렌더링 하도록 요청하는 함수이다.
- 이 함수는 새로운 상태값이나 이전 상태값을 받는다.
- setState를 호출하면 React는 상태 값을 업데이트하고 관련된 컴포넌트를 다시 렌더링한다.
1. 새로운 상태값 VS 이전 상태값 받기
- setState 함수를 쓸 때 아래와 같은 코드를 많이 봤을 것이다. 무슨 차이가 있을까?
const [state1, setState1] = useState({
id: '',
name: '',
});
const [state2, setState2] = useState({
id: '',
name: '',
});
setState1({ ...state1, name: e.target.value });
setState2((prevState2) => ({ ...prevState2, name: e.target.value }));
🔸setState(newState)
setState(newState);
- setState() 를 호출 할 때마다 컴포넌트의 상태가 'newState '로 업데이트 되고, 리액트는 변경된 상태를 반영하기 위해 컴포넌트를 다시 렌더링한다.
🔸setState((prevState)=>{return newState})
setState((prevState) => {
// 이전 상태를 기반으로 새로운 상태를 계산
return newState;
});
- 'setState()' 함수에 전달되는 인자는 이전 상태를 받아 새로운 상태를 계산하는 콜백함수이다.
prevState는 이전 상태를 나타내는 객체이다. 리액트는 이전 상태와 반환된 새로운 상태를 비교하여 변경사항을 감지하고 컴포넌트를 다시 렌더링 한다.
먼말이지? 위에 설명을 봐도 감이 안올 것이다.
아래 코드를 보자.
🔸setState(newState) 를 동일하게 호출한 경우
import { useState } from "react";
const InputPrac = () => {
const [value, setValue] = useState(0); // value = 3 6 9 ...
const addNum = (e) => {
setValue(value + 1);
setValue(value + 2);
setValue(value + 3);
};
return (
<>
<p>{value}</p>
<button onClick={addNum}>클릭</button>
</>
);
};
export default InputPrac;
위 코드를 봤을 때 addNum 함수 안에 setValue가 3개나 있다. 따라서 우리는 아래 코드박스처럼 클릭버튼을 누를 때마다 6,12,18 로 가지 않을까? 라고 예상 했을 것이다.
setValue(0+1)
setValue(1+2)
setValue(3+3)
하지만, 실제로는 3,6,9만 반영이 된다. 즉, setValue(0+3) 맨마지막 업데이트 된 state로만 반영이 된다.
🤔왜그런걸까?
- React 의 useState 훅은 즉시 state를 업데이트 하지 않는다. 'setState' 함수를 호출하더라도 해당 상태가 즉시 변경되지 않는다는 뜻이다.
- React 는 상태 변경 요청을 일정 기간동안 '모아서' 비동기적으로 처리한다.
- 왜 그렇게 모아서 하는데? React 가 최적화를 통해 컴포넌트의 리렌더링을 효율적으로 수행하기 위해서다.
최적화를 하지 않을 경우, 짧은 시간 동안 여러 번의 상태 변경이 요청되면 한 state가 업데이트 될 때마다 리렌더링을 해야할 것이다. 이는 비효율적이다.
따라서, React는 이들 요청을 한 번의 배치 업데이트로 묶어서 처리한다. 즉, 이벤트 발생시 실행되는 콜백함수 내에서의 state의 가장 마지막 갱신 내용으로 state를 한번만 업데이트 시키게 된다.
- 하지만 이런 최적화 방법은 상태 변경이 즉시 반영되지 않아서 주의 해야한다. 따라서, 이전 상태에 의존하는 새 상태를 계산하는 경우, setState 함수에 콜백함수를 전달하여 이 문제를 해결할 수 있다. 이 콜백함수는 이전 상태를 매개변수로 받아 새 상태를 반환하기에 상태 변경이 즉시 일어나지 않더라도, 항상 정확한 이전 상태를 기반으로 새상태를 계산할 수 있다.
아래 해결책을 봐보자
🔸setState 인자에 콜백함수를 넣어 여러번 호출할 경우
const InputPrac = () => {
const [value, setValue] = useState(0); // value = 6 12 18 ...
const addNum = (e) => {
setValue((prev) => prev + 1); // prev = 0 // updated value = 1
setValue((prev) => prev + 2); // prev = 1 // updated value = 3
setValue((prev) => prev + 3); // prev = 3 // updated value = 6
};
return (
<>
<p>{value}</p>
<button onClick={addNum}>클릭</button>
</>
);
};
- setstate를 콜백함수 형식으로 쓴다면, setstate의 인자로 업데이트 된 state값을 받아올 수 있다.
- 하지만, setstate함수를 통해 갱신된 state가 DOM에 리렌더 되는 때는, addNum 콜백함수 본문이 다 끝나고 나서 반영되기 때문에, 최종 업데이트된 값인 6, 12 , 18로 보여지게 되는 것이다.
2. 비동기적으로 처리되는 setState 함수
- 위에 언급했듯이 setState함수는 비동기적으로 처리된다.
- state는 값이 변경되면 리렌더링이 발생한다. 그런데 변경되는 state가 많을 경우 리렌더링이 계속 일어날테고 속도도 저하될것이다. 따라서 React는 성능을 위해서 setState() 호출을 변경된 값들을 모아 단일 업데이트로 한꺼번에 진행하여 렌더링을 줄이고자 배치(Batch) 기능을 사용해 비동기로 작동한다고 볼 수 있다. 배치 업데이트는 16ms 주기이다. 비동기적으로 업데이트 될 수 있기 때문에 다음 state를 사용할 때 해당 값에 의존해서는 안 된다.
따라서, 동기적으로 실행하고 싶을 땐 useEffect 를 쓰면 된다. useEffect 는 컴포넌트가 렌더링/재렌더링 될때 실행되는 함수이다. [state] 가 변경될 때 useEffect 안의 코드 실행해주세요~ 라는 뜻이다.
useEffect(()=>{
}, [state])
참고자료
https://1yoouoo.tistory.com/16
'프론트엔드 개발 > React' 카테고리의 다른 글
리액트) state의 모든 것 (props/state/로컬/전역/서버상태) (0) | 2023.08.16 |
---|---|
React-Query 사용 방법 (0) | 2023.07.11 |
React) 커스텀 훅(Custom Hook) 만들기 (0) | 2023.07.10 |
React) Recoil 사용법 (0) | 2023.06.24 |
React) useRef 사용 예제 (0) | 2023.06.23 |