Skip to content

bug(react-native): useAskableVisibility and useAskableScrollView leak context when ctx prop changes #250

Closed as not planned

Description

@vamgan

Summary

Both useAskableVisibility and useAskableScrollView create an internal context via useState when no ctx is provided. The cleanup effect is supposed to destroy that internal context on unmount — but the cleanup condition checks the options parameter ctx, not the state variable, causing it to always run (or never run) incorrectly if the parent later provides a different context.

Root cause

packages/react-native/src/useAskableVisibility.ts:

const [visibilityCtx] = useState<AskableContext>(
  () => ctx ?? createAskableContext(options)
);

useEffect(() => {
  return () => {
    if (!ctx) {           // ← checks the CURRENT options.ctx prop
      visibilityCtx.destroy(); // ← but visibilityCtx was created based on ctx at mount time
    }
  };
}, [ctx, visibilityCtx]);

If a parent passes ctx={undefined} at mount (so an internal context is created) and later passes ctx={someCtx}, the cleanup will NOT destroy the original internal context because at cleanup time ctx is now truthy.

The identical pattern exists in useAskableScrollView.ts.

Expected behavior

Whether to destroy the internal context should be decided at creation time (was a context created internally?) and stored alongside it, not re-evaluated at cleanup time from the current prop value.

Fix

const [{ visibilityCtx, owned }] = useState(() => {
  const provided = ctx;
  return provided
    ? { visibilityCtx: provided, owned: false }
    : { visibilityCtx: createAskableContext(options), owned: true };
});

useEffect(() => {
  return () => {
    if (owned) visibilityCtx.destroy();
  };
}, [visibilityCtx, owned]);

Same fix applies to useAskableScrollView.

Activity

  1. added a commit that references this issue on Jun 1, 2026
    b4beb20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions