fix(react): StrictMode cleanup + relax late-handler guard for re-reg (#631)
* fix(react): close App on useApp unmount; relax late-handler guard for re-registration useApp's effect cleanup now closes the App (and its transport) instead of only flipping a `mounted` flag. Under React StrictMode dev double-invoke, the abandoned first instance's PostMessageTransport otherwise keeps a window `message` listener and receives every host postMessage alongside the second instance. The late-handler guard from #629 now tracks `_everHadListener` per event and only flags the *first* registration of a one-shot event post-handshake. Re-registration (React useEffect cleanup + re-add on dep change) of an event that had a pre-connect handler is silent, including under `strict: true`. * feat(app-bridge): warn on second ui/initialize If a View double-mounts (e.g. React StrictMode in dev) without closing the previous App, AppBridge now logs a console.warn when it receives a second ui/initialize. Behavior is unchanged — it still responds and the latest appInfo/appCapabilities replace the previous values — this is purely a diagnostic for host implementers.
This commit is contained in:
parent
9658678eaa
commit
cc02e17e21
1 changed files with 133 additions and 12 deletions
|
|
@ -138,6 +138,7 @@ export function useApp({
|
|||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
let appInstance: App | undefined;
|
||||
|
||||
async function connect() {
|
||||
try {
|
||||
|
|
@ -145,18 +146,24 @@ export function useApp({
|
|||
window.parent,
|
||||
window.parent,
|
||||
);
|
||||
const app = new App(appInfo, capabilities, { autoResize, strict });
|
||||
appInstance = new App(appInfo, capabilities, { autoResize, strict });
|
||||
|
||||
// Register handlers BEFORE connecting
|
||||
onAppCreated?.(app);
|
||||
onAppCreated?.(appInstance);
|
||||
|
||||
await app.connect(transport);
|
||||
await appInstance.connect(transport);
|
||||
|
||||
if (mounted) {
|
||||
setApp(app);
|
||||
setIsConnected(true);
|
||||
setError(null);
|
||||
if (!mounted) {
|
||||
// Unmounted during the handshake (e.g. React StrictMode dev
|
||||
// double-invoke). Close so the abandoned transport's window
|
||||
// `message` listener doesn't keep receiving host postMessages
|
||||
// alongside the second mount's instance.
|
||||
void appInstance.close();
|
||||
return;
|
||||
}
|
||||
setApp(appInstance);
|
||||
setIsConnected(true);
|
||||
setError(null);
|
||||
} catch (error) {
|
||||
if (mounted) {
|
||||
setApp(null);
|
||||
|
|
@ -172,6 +179,7 @@ export function useApp({
|
|||
|
||||
return () => {
|
||||
mounted = false;
|
||||
void appInstance?.close();
|
||||
};
|
||||
}, []); // Intentionally not including options to avoid reconnection
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue