[BFE.dev] async event handler

치만·2026년 1월 18일

BFE.dev react

목록 보기
14/28
post-thumbnail

async event handler

// This is a React Quiz from BFE.dev 

import * as React from 'react'
import { useState } from 'react'
import { createRoot } from "react-dom/client";
import { screen, fireEvent } from "@testing-library/dom"

function App() {
  const [state, setState] = useState(0)
  const increment = () => {
    setTimeout(() => {
      setState(state + 1)
    }, 100)
  }
  console.log(state)
  return <div>
    <button onClick={increment}>click me</button>
  </div>
}

const root = createRoot(document.getElementById('root'))
root.render(<App/>)

;(async () => {
  const action = await screen.findByText('click me')
  // click the button twice
  fireEvent.click(action)
  await wait(50);
  fireEvent.click(action)
  await wait(50);
})()

function wait(duration = 100) {
  return new Promise((resolve) => setTimeout(resolve, duration));
}

출력 결과

0
1
1

풀이 과정

1. 초기 렌더링

  • 0 출력: App이 처음 실행될 때 state0입니다.

  • 첫번째 클릭 (t = 0ms): setTimeout이 예약됩니다. 이 타이머 안의 콜백 함수는 생성 당시의 state0을 가지고 있습니다.

  • 두 번째 클릭 (t = 50ms): 두 번째로 실행된 increment 내부의 타이머도 동일하게 생성 당시의 state0을 가집니다.


2. 상태 업데이트 후 재렌더링 (t=100ms)

  • 1 출력: 첫 번째 타이머가 종료되면서 setState(0 + 1)을 실행합니다.

3. 상태 업데이트 후 재렌더링 (t=150ms)

  • 1 출력: 두 번째 타이머가 종료되면서 setState(0 + 1)을 실행합니다.

    increment 함수가 선언될 때의 state 값(0)을 setTimeout이 그대로 가져갔기 때문에 state 값이 2가 되지 않습니다


🤔 만약 2로 만들고 싶다면?

함수형 업데이트(Updater function)를 사용해야 합니다.

// 기존 방식 (문제 발생)
setState(state + 1) 

// 해결 방식 (항상 최신 상태 참조)
setState(prevState => prevState + 1)
profile
🌱개발 기록장

0개의 댓글