GitHub
Open Github

Doorway

Connect conditional rendering to side effects.

Installation

pnpm add @hookraft/doorway

useDoorway

The core hook. Manage all five lifecycle states in one place.

import { useDoorway } from "@hookraft/doorway"
const door = useDoorway({
  onEnter:   () => fetchData(),
  onLoading: () => showSpinner(),
  onSuccess: () => trackEvent(),
  onError:   () => alertUser(),
  onExit:    () => clearData(),
})

Full Example

function CheckoutPage() {
  const [order, setOrder] = useState(null)

  const door = useDoorway({
    onEnter: () => door.load(),
    onLoading: async () => {
      try {
        const data = await fetchOrder()
        setOrder(data)
        door.succeed()
      } catch {
        door.fail()
      }
    },
    onSuccess: () => analytics.track("checkout_viewed"),
    onError: () => toast.error("Failed to load order"),
    onExit: () => setOrder(null),
  })

  return (
    <div>
      <button onClick={door.enter}>View Order</button>
      <button onClick={door.exit}>Close</button>

      {door.is("loading") && <Spinner />}
      {door.is("success") && <OrderSummary order={order} />}
      {door.is("error") && <button onClick={door.load}>Retry</button>}
    </div>
  )
}

Options

PropTypeDefault
onEnter
() => void
-
onExit
() => void
-
onLoading
() => void
-
onSuccess
() => void
-
onError
() => void
-

Returns

PropTypeDefault
status
DoorwayStatus
-
is(status)
(s: DoorwayStatus) => boolean
-
enter()
() => void
-
exit()
() => void
-
load()
() => void
-
succeed()
() => void
-
fail()
() => void
-
reset()
() => void
-

Doorway Component

The declarative JSX wrapper. Wrap your components and declare lifecycle logic inline — no hook required for simple cases.

import { Doorway } from "@hookraft/doorway"
<Doorway
  when={isOpen}
  onEnter={() => fetchData()}
  onExit={() => clearData()}
  fallback={<p>Nothing here yet</p>}
>
  <Modal />
</Doorway>

With Status States

Combine with useDoorway for full lifecycle control:

function App() {
  const door = useDoorway({
    onEnter:   () => door.load(),
    onLoading: () => fetchData().then(door.succeed).catch(door.fail),
    onExit:    () => clearData(),
  })

  return (
    <>
      <button onClick={door.enter}>Open</button>

      <Doorway when={door.status !== "idle" ? door.status : false}>
        {door.is("loading") && <Spinner />}
        {door.is("success") && <Content />}
        {door.is("error") && <Error />}
      </Doorway>
    </>
  )
}

Props

PropTypeDefault
when
boolean | DoorwayStatus
-
onEnter
() => void
-
onExit
() => void
-
onLoading
() => void
-
onSuccess
() => void
-
onError
() => void
-
fallback
ReactNode
null
children
ReactNode
-