React에서 상태값을 변경시켜주는 setState는 기본적으로 비동기적
으로 작동한다.
리액트는 브라우저 렌더링이 끝날 때 쯤 일괄적으로 상태값들을 업데이트한다는 특성을 가지고 있다.
혹은 연속적으로 state 변경이 일어날 때 일괄 처리를 통해서 16ms의 시간동안 변경된 값들을 모아 한 번만 렌더링을 진행하게 된다!
const [test, setTest] = useState(1);
const handleUpdate = () => {
setTest(test + 1);
setTest(test + 1); // 일괄 처리로 인해 마지막 setState만 적용되어 최종적으로 test는 2
console.log(test) // 출력: 1
}
이런 코드가 있다고 할 때, setTest 라는 비동기함수가 연속적으로 실행되었고 일괄 처리
특성으로 인해 setTest(test+1)가 모아져 한 번만 실행된다.
그리고 handleUpdate라는 함수의 로직이 끝나지 않았기 때문에 그 내부에서 state값을 불러와 출력한다면 아직 업데이트되지 않은 기본값 1로 표시된다!
그래서 이전 값과 관련되어 값을 연속적으로 업데이트하고 싶을 땐 prev 기능을 사용하여 이전 값에 접근할 수 있도록 코드를 작성해야 한다.
📌 즉, 리액트는 불필요한 렌더링을 줄이고자 일괄적인 업데이트를 진행하는 특성을 가지고 있다.
"불필요한 렌더링을 줄이려고 당연히 상태값 변경이 비동기적으로 일어나겠지!" - 과거의 나 ..
setState 개념과 관련하여 반대로 생각해보려는 고민을 해보지 않았다...😔
그저 '불필요한 렌더링을 줄이려고 비동기적으로 작동하는거라면, 그 반대일 땐 지속적으로 렌더링이 일어나서 효율성이나 사용자 경험이 떨어지겠지' 라고 단순하게 접근했다.
겉햝기식의 고민이었던 것 같다.
참고: Github-왜 setState는 비동기적으로 작동하는 것인가?, Merlin Tech Blog
해당 깃허브의 해외 개발자분들이 남긴 의견을 읽어보면서, 조금 이해가 되지 않아 이를 풀어쓴 타 개발자님의 블로그를 참고하여 이유를 이해하게 되었다.
만약 setState가 동기적이라면 ...
state 값들은 동기적으로 업데이트되지만, 이 state 값들을 넘겨받는 props는 해당되지 않는다고 한다.
즉, 본인에게 값을 넘겨주는 부모 컴포넌트가 렌더링이 되기 전까진 props를 알 수 없는 상태가 된다.
참고 자료에서는 다음과 같은 코드를 예시로 들고 있다.
[자식 컴포넌트]
this.props.onIncrement(); // props로 넘어오는 value가 0이고, 숫자 + 1을 하는 함수가 있다고 가정
console.log(this.props.value); // 0
this.props.onIncrement();
console.log(this.props.value); // 0
this.props.onIncrement();
console.log(this.props.value); // 0
[만약 props가 아닌 state를 바로 변경했을 땐]
console.log(this.state.value); // 0
this.setState({ value: this.state.value + 1 });
console.log(this.state.value); // 1
this.setState({ value: this.state.value + 1 });
console.log(this.state.value); // 2
state는 바로 동기적으로 업데이트되지만, 위에 작성한 설명처럼 state가 아닌 넘겨받는 props 값을 동기적으로 실행시키면 예상과 다르게 업데이트되지 않는다.
왜냐하면 props 값들이 업데이트되기 위해선 props 값을 넘겨주는 부모 컴포넌트가 항상 렌더링되어야 하기 때문이다.
console.log(this.props.value); // 0
this.props.onIncrement();
console.log(this.props.value); // 0
this.props.onIncrement();
console.log(this.props.value); // 0
즉, props.value 값을 업데이트 시키고 싶다면 props.onIncrement() 함수가 실행될 때마다 부모컴포넌트도 같이 실행되어야 하고 자연스럽게 성능 저하로 이어질 수 있음을 생각할 수 있다.
그리고 가상 돔은 Reconciliation(재조정)
이란 특성을 가지고 있다.
이 특성은 변경된 부분을 파악 후 state, props가 모두 일괄적으로 업데이트될 수 있도록 도와주게 된다.
또 업데이트가 동기적이라면,
우선순위 상관없이 순차적으로 코드가 실행되기 때문에 사용자 경험면에서도 좋지 않은 결과를 가져올 것 같다고 생각한다.