Skip to content

clear modal stack synchronously to survive host unmount#202

Open
kashif-javaid-plt wants to merge 1 commit into
colorfy-software:mainfrom
kashif-javaid-plt:fix/closeAllModals-queue-race
Open

clear modal stack synchronously to survive host unmount#202
kashif-javaid-plt wants to merge 1 commit into
colorfy-software:mainfrom
kashif-javaid-plt:fix/closeAllModals-queue-race

Conversation

@kashif-javaid-plt
Copy link
Copy Markdown

Summary

Fixes a race where modalfy().closeAllModals() fails to clear the stack when the
calling component unmounts right after the call (e.g. navigating away on the same
tick). Modals stay visible on the next screen.

Root cause

modalfy().closeAllModals routed through queueClosingAction, which only drains
after the topmost <StackItem>'s exit animation completes. If the StackItem
unmounts before the animation finishes, the queued action is dropped and
openedItems is never cleared.

Also fixes a latent stale-closure bug in ModalProvider: the subscription listener
is defined outside useEffect but only subscribed once on mount, so
setContextValue({ ...contextValue, ... }) captured stale contextValue and
back-to-back state updates could silently revert to stale state.

Changes

  • src/lib/ModalState.tsmodalfy().closeAllModals now calls
    ModalState.closeAllModals() directly and invokes the callback synchronously. The
    queued path still runs from handleBackPress, which legitimately needs the
  • src/lib/ModalProvider.tsx — switch the subscription listener to a functional
    setContextValue(prev => ...) updater so updates always compose against the latest
    state.

Repro (before the fix)

  1. Open one or more modals from a screen.
  2. Call modalfy().closeAllModals() and immediately navigate away (causing the host
    component to unmount synchronously).
  3. Navigate back — modals from step 1 are still mounted / visible.

After the fix, step 3 yields an empty stack as expected.

Notes for reviewers

  • Only src/ was modified. lib/ is regenerated by yarn prepare (bob build) at
    release time and is intentionally not in this diff.
  • yarn type, yarn lint, and yarn test all pass.
  • No public API change; modalfy().closeAllModals(callback) still accepts and calls
    the callback — it just fires synchronously now instead of after an animation.

  fix: clear modal stack synchronously to survive host unmount

  Body:
  modalfy().closeAllModals() routed through queueClosingAction, which
  only drains after the topmost <StackItem>'s exit animation finishes.
  If the calling component unmounts the host right after (e.g. a
  navigation transition), the queued action is dropped and openedItems
  is never cleared — modals stay visible on the next screen.

  Fix: call ModalState.closeAllModals() directly from modalfy() and
  invoke the callback synchronously. The queued path still runs from
  handleBackPress, which legitimately needs the animation.

  Also fix a latent stale-closure bug in ModalProvider: the subscription
  listener is defined outside useEffect but only subscribed once, so
  setContextValue({ ...contextValue, ... }) could capture stale state
  on back-to-back updates. Switch to a functional updater.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant