Home [React] useState 왜 비동기로 만들었을까?
Post
Cancel

[React] useState 왜 비동기로 만들었을까?

useState 왜 비동기로 만들었을까?

React 에서 state 는 컴포넌트 내부의 변경 가능한 값이다.

일반적으로 사용하는 변수를 놔두고, 왜 state 를 사용해서 값을 관리할까?

state 가 갖는 특성 때문이다. 바로 값이 변경되면 리렌더링(Re-rendering) 이 발생하기 때문이다.

-> 따라서 값이 변화함에 따라 실시간으로 화면이 렌더링되고 변화된 값이 화면에 바로 반영된다.

값의 변화를 리액트도 알아차릴 수 있게 해주어야 하므로 값의 변경은 리액트가 제공하는 함수를 통해서만 이루어져야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React, {useState } from 'react';

const MyFunc = () => {
	const [cnt, setCnt] = useState(0) // 초기화 0

	const plusNum = () => {
		setCnt( cnt +1 );
	}

return (
	<div>
		<p> : {cnt} </p>
		{/*훅이 반환하는 함수를 통해서만 값을 변경해야 함 */}
		<button onClick={plusNum>plus</button>
	</div>
	)
}

plusNum() 함수에 콘솔을 추가해보자.

1
2
3
4
const plusNum = () => {
  setCnt(cnt + 1);
  console.log("result:" + cnt);
};

의도한 것은 state 에 cnt+1. 의 값을 설정하고 새로 설정된 값을 바로 콘솔창에 출력하고 싶다. 하지만 결과는 한 걸음 늦는다.

result: 0

왜 이럴까? 바로 비동기(Asynchronous)의 특성 때문이다.

동기로 처리하기

비동기 특성을 갖는 이벤트 루프에 의해 setCnt 작업은 뒤로 밀리고, console.log 작업이 먼저 실행되기 때문이다.

이벤트 루프가 비동기로 업무를 처리하기 때문에.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const printSequence = () => {
	console.log('first Call')
	setTimeout(() => {secondCall()}, 0)
	console.log('third Call');
}

const secondCall = () => {
	console.log('second Call');
}


결과는 다음과 같다.
first Call
third Call
second Call

setTimeout() 함수도 비동기적으로 작동하기 때문이다.

그러면 setTimeout 을 통해 console.log 작업시간을 뒤로 연기하면 작업이 끝난 뒤 로그를 찍을테니 값이 제대로 표시될까?

일단 정답부터말하면 NO 다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const showAlert = () => {
	setCnt(cnt+1);
	setTimeout(() => {
		console.log('result(3sec):' +cnt)}
		, 3000);
	}
 함수를 5 실행해보자 .

결과는 다음과 같다.

result(3sec): 0
result(3sec): 1
result(3sec): 2
result(3sec): 3
result(3sec): 4

함수를 다섯번 실행했으니 콘솔도 5까지 찍혀야 하지만 , 업데이트 되기 전의 값이 나온다.

Why?

이거는 그냥 setTimeout 함수의 특성 때문이다. 함수가 실행할 때 마다 새로운 함수가 만들어지고, 전달되는 함수 내부의 값은 변수가 아닌 전달하는 시점의 변수값(상수) 이 전달되기 때문이다.

따라서 1000초뒤에 실행하더라도 실행되는 값은 전달하는 시점의 변수값이다.

해결방법

useEffect 를 사용하면된다.

1
2
3
4
5
6
7
consts plusNum = () => {
	setCnt(cnt+1);
}

useEffect(() => {
	console.log(cnt);
}, [cnt])

디펜던시가 cnt 이므로 , cnt 가 변경될때마다 (즉 변경 후 ) 콘솔 함수가 실행되므로, 의도된 값을 출력할 수 있다.

비동기로 작동하는 이유

state 값이 변경되면 리렌더링 발생.

여러개의 값이 변경될때마다 렌더링이 된다면, 엄청나게 많은 수의 렌더링을 하게 될 것이다.

따라서 변경된 값들을 모아 한번에 업데이트를 진행하여 렌더링을 줄이고자 배치(Batch)기능을 사용해 비동기로 작동시키는 것이다.

배치업데이트는 16ms 주기라고 한다.

참조

https://choonse.com/2022/01/21/677/

This post is licensed under CC BY 4.0 by the author.

[React] Event Capturing, Bubbling

[JS][OOP] 객체지향 5원칙(SOLID)- 단일 책임 원칙 (Single Responsibility Principle)