Skip to Content
use-async-flow

Background

모달을 구현할 때 선언적(declarative) vs 명령형(imperative) 접근 간의 논의는 항상 뜨겁습니다.

// 선언적 모달의 사용 const App = () => { const [isOpen, setIsOpen] = useState(false) return ( <> <button onClick={() => setIsOpen(true)}>Open</button> <Modal isOpen={isOpen} onClose={() => setIsOpen(false)} /> </> ) } // 명령형 모달의 사용 const handleClick = async () => { const isConfirmed = await openModal(({ onClose }) => <Modal onClose={onClose} />) // ... }
  • 선언적 모달: 컴포넌트 트리 안에서 isOpen, onClose 같은 props로 상태를 드러내고 관리하기 쉽습니다. 하지만 특정 시점의 상호작용(예: “이 버튼 클릭 후 모달의 사용자 응답을 기다렸다가 다음 로직 진행”)을 구현하려면 코드가 장황해지거나 상태/콜백이 흩어집니다.
  • 명령형 모달: open()을 호출하고 await으로 결과를 받아 다음 로직을 자연스럽게 이어갈 수 있습니다. 다만 모달이 트리의 다른 곳에 렌더되기 때문에 일반적인 리액트 상태 흐름을 따르지 않습니다. 이는 Context API 사용이나 prop 전달을 어렵게 합니다.

use-async-flow는 이 둘의 절충안을 지향합니다.

  • 선언적으로 마운트: 모달은 컴포넌트 트리에서 렌더/제어됩니다. 스타일, 레이아웃은 App의 관심사로 유지됩니다.
  • 명령적으로 컨트롤: 훅이 open() / resolve() / dismiss()를 제공하고, open()은 Promise로 결과를 돌려줍니다. 따라서 “사용자 응답을 기다린 뒤 다음 로직 실행” 같은 흐름을 간결하게 표현할 수 있습니다.

더불어 아래의 개선사항을 포함합니다.

  • 예측 가능한 결과 모델: 결과는 resolved(확정) 또는 dismissed(취소/닫힘)로 표준화되어 안전한 분기와 로깅이 가능합니다.

즉, 이 훅은 overlay(특히 모달)의 상태 관리 라이브러리입니다. 선언적 UI 구조를 유지하면서도, 명령형으로 “열기 → 기다림 → 닫힘/확정”의 사용자 흐름을 간결하게 작성하도록 돕습니다.

import { useAsyncFlow } from 'use-async-flow' function Example() { const overlay = useAsyncFlow<boolean, 'esc' | 'backdrop'>() return ( <> <button onClick={async (e) => { const result = await overlay.open(e) if (result.status === 'resolved' && result.value === true) { alert('confirmed!') } else if (result.status === 'resolved' && result.value === false) { alert('rejected!') } else if (result.status === 'dismissed' && result.reason === 'esc') { alert('esc pressed!') } else if (result.status === 'dismissed' && result.reason === 'backdrop') { alert('backdrop clicked!') } else if (result.status === 'dismissed' && result.reason === 'unmount') { alert('unmounted!') } }} > Open </button> <Modal isOpen={overlay.isOpen} onConfirm={() => overlay.resolve(true)} onReject={() => overlay.resolve(false)} onESCPress={() => overlay.dismiss('esc')} onBackdropClick={() => overlay.dismiss('backdrop')} /> </> ) }

Live Demo

다음은 use-async-flow를 사용해 모달 2개를 순차적으로 열고 결과를 받는 데모입니다.

코드는 여기 에서 확인할 수 있습니다.

const r1 = await overlay1.open(event) const r2 = await overlay2.open(event)

아래 버튼을 눌러 실제로 실행해보세요
상태모달1 closed모달2 closed
최근 결과
아직 결과가 없습니다.

Installation

npm install use-async-flow

API

자세한 내용은 API 레퍼런스 페이지를 확인하세요: API Reference

Last updated on