[React]useState 비동기(+동기처리 방법)

useState를 사용하면서 내가 의도한대로 값이 변경되지 않는 경우가 종종 있었다.

state의 값이 변경되기 전에 값을 가져와서 사용했기 때문에 일어난 일이다.

 

useState는 비동기로 동작한다.

정확히 말하자면 useState의 setState가 비동기로 동작하는 것이다.

import React, { useState } from "react";

const App = () => {
  const [num, setNum] = useState(0);
  const increaseNum = () => {
    setNum(num + 1);
    setNum(num + 1);
    setNum(num + 1);
    console.log(num);
  };
  return (
    <div style={{ margin: "100px" }}>
      <p>{num}</p>
      <button onClick={increaseNum}>+3</button>
    </div>
  );
};

export default App;

useState가 비동기로 동작한다는 사실을 모르고 코드를 보면,

버튼을 눌렀을 때 3이 증가하고 콘솔창에 3이 출력될 것이라고 예상할 수 있다.

하지만 버튼을 누르면 1이 증가되고, 콘솔창에는 0이 출력된다.

 

⭐️ useState가 비동기적으로 동작하는 이유

react에서 state가 변화하면 state와 관련된 화면이 리렌더링 된다.

한 페이지에는 수많은 state들이 존재하고, state 하나가 변경될때마다 한 번씩 화면이 리렌더링 된다면 성능저하가 일어날 것이다.

이런 문제 때문에 렌더링을 줄이고자 react에서는 setState가 호출되면 배치 처리를 통해 한 번에 렌더링해준다.

 

여기서 배치 처리란 

실시간으로 처리하지 않고 16ms 동안 변경된 상태 값들을 하나로 묶어서 처리하는 것을 의미한다.

 

이러한 이유로 버튼을 눌렀을 때, num에 +3이 아닌 +1이 되는 것이다.

또한 setState는 비동기 작업이기 때문에 이벤트 루프에 의해 뒤로 밀려나고,

console.log가 먼저 실행되어서 콘솔 창에는 변화되기 전의 값인 0이 출력되는 것이다.

 

⭐️ 동기로 처리하는 방법

setState 사용 시, 인자 값으로 함수를 전달하는 방법이다.

import React, { useState } from "react";

const App = () => {
  const [num, setNum] = useState(0);
  const increaseNum = () => {
    setNum((num) => num + 1);
    setNum((num) => num + 1);
    setNum((num) => num + 1);
  };
  return (
    <div style={{ margin: "100px" }}>
      <p>{num}</p>
      <button onClick={increaseNum}>+3</button>
    </div>
  );
};

export default App;

 

(num) => num + 1 으로 변경해주었다.

이렇게 변경해주면 업데이트된 state 값을 setState의 인자로 받기 때문에 의도한대로 동작하도록 할 수 있다.

 

알고 보면 간단한 것 같지만 제대로 알기 전까지는 헷갈렸던 부분이다.

헷갈리는 부분이 있으면 한 번 짚고 넘어가는게 좋은 것 같다!!