You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
description: "Build modals/dialogs self-contained: form and in-flight state lives inside, closing unmounts it. Use when writing or reviewing a modal/dialog, especially one that owns async work (OAuth popups, timers, subscriptions, AbortControllers). Catches the stuck-on-Connecting class of lifecycle-leak bugs."
4
+
---
5
+
6
+
# Self-contained modals
7
+
8
+
A modal's **form state and in-flight work live INSIDE the modal**, and **closing
9
+
the modal UNMOUNTS that state** so it is destroyed, not hand-reset. Only the
10
+
**open/route intent** belongs to the parent (deep links, programmatic open,
11
+
reconnect handoffs need it).
12
+
13
+
## Why
14
+
15
+
A hand-written `reset()` has to enumerate every field, and it silently drifts out
16
+
of sync with state owned by **child hooks the parent can't see**. Unmounting
17
+
resets everything for free, including child-hook cleanup effects (cancelling a
18
+
dangling server session, clearing a timer, aborting a fetch).
19
+
20
+
Concrete bug this prevents (executor, add-account-modal): the modal was mounted
21
+
unconditionally, so `useOAuthPopupFlow`'s `busy` survived close. `reset()` zeroed
22
+
its own booleans but never called `oauthPopup.cancel()`. Abandon the OAuth popup,
23
+
close, reopen, and `oauthBusy = false || busy(true) = true`, so the footer is
24
+
wedged on "Connecting…" with Close disabled. Unmounting would have cleared `busy`
25
+
AND run the hook's cleanup that cancels the server OAuth session.
26
+
27
+
## How to apply
28
+
29
+
Default to **genuine conditional unmount**, state inside. When closed the
30
+
component returns `null`, so React destroys all of it and runs every child
31
+
hook's cleanup. This is the cleanest fix and the one to reach for first:
Copy file name to clipboardExpand all lines: apps/cli/CHANGELOG.md
+49Lines changed: 49 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,54 @@
1
1
# executor
2
2
3
+
## 1.5.21
4
+
5
+
### Patch Changes
6
+
7
+
-[#1134](https://github.com/RhysSullivan/executor/pull/1134)[`78aa871`](https://github.com/RhysSullivan/executor/commit/78aa8710d774d552d6030eca060c5e72f0899461) Thanks [@RhysSullivan](https://github.com/RhysSullivan)! - Fix OAuth callbacks in cloud so they preserve the URL-selected organization when the session cookie points at another org.
8
+
9
+
- Updated dependencies []:
10
+
-@executor-js/local@1.4.4
11
+
-@executor-js/sdk@1.5.21
12
+
-@executor-js/runtime-quickjs@1.5.21
13
+
-@executor-js/api@1.4.41
14
+
15
+
## 1.5.20
16
+
17
+
### Patch Changes
18
+
19
+
-[#1132](https://github.com/RhysSullivan/executor/pull/1132)[`580fc7f`](https://github.com/RhysSullivan/executor/commit/580fc7f8b2615a0d7760b3a4daddf8d45673ef3f) Thanks [@RhysSullivan](https://github.com/RhysSullivan)! - Fix the PostHog custom MCP OAuth setup flow so Add connection opens PostHog authorization instead of falling back to manual OAuth app registration.
20
+
21
+
- Updated dependencies []:
22
+
-@executor-js/sdk@1.5.20
23
+
-@executor-js/runtime-quickjs@1.5.20
24
+
-@executor-js/local@1.4.4
25
+
-@executor-js/api@1.4.40
26
+
27
+
## 1.5.19
28
+
29
+
### Patch Changes
30
+
31
+
-[#1115](https://github.com/RhysSullivan/executor/pull/1115)[`92bd86c`](https://github.com/RhysSullivan/executor/commit/92bd86cb975ce867b3002ae9bcb6bf60da67cc48) Thanks [@RhysSullivan](https://github.com/RhysSullivan)! - Google media downloads (Drive file contents, exports, and other binary
32
+
endpoints) are now returned as binary responses instead of being decoded as
33
+
text, so files come back intact. Emit them with `emit(result.data)`.
34
+
35
+
-[#1115](https://github.com/RhysSullivan/executor/pull/1115)[`92bd86c`](https://github.com/RhysSullivan/executor/commit/92bd86cb975ce867b3002ae9bcb6bf60da67cc48) Thanks [@RhysSullivan](https://github.com/RhysSullivan)! - The CLI now validates that a URL is `http`/`https` before handing it to the
36
+
operating system's browser opener, and on Windows opens it via
37
+
`rundll32 url.dll,FileProtocolHandler` instead of `cmd /c start`. This removes a
38
+
path where a crafted URL could be interpreted as a shell command. `executor
39
+
login` and the "open in browser" prompts behave the same for normal URLs.
40
+
41
+
-[#1115](https://github.com/RhysSullivan/executor/pull/1115)[`92bd86c`](https://github.com/RhysSullivan/executor/commit/92bd86cb975ce867b3002ae9bcb6bf60da67cc48) Thanks [@RhysSullivan](https://github.com/RhysSullivan)! - Hardened the hosted egress guard. Outbound requests from OAuth token exchanges,
42
+
MCP transports, and GraphQL/Google/Microsoft discovery now all route through the
43
+
guard, and the guard resolves DNS before connecting so a hostname that points at
44
+
a private or loopback address is blocked rather than only literal private IPs.
45
+
This tightens SSRF protection for hosted and cloud execution.
0 commit comments