diff --git a/.github/lynx-stack.instructions.md b/.github/lynx-stack.instructions.md index 023e0c6ec5..1fe6fe7caa 100644 --- a/.github/lynx-stack.instructions.md +++ b/.github/lynx-stack.instructions.md @@ -6,3 +6,9 @@ When updating web element APIs, add targeted Playwright tests in packages/web-pl Ensure Playwright browsers are installed (pnpm exec playwright install --with-deps ) before running web-elements tests. For x-input type="number" in web-elements, keep inner input type as text, set inputmode="decimal", and filter number input internally without setting input-filter explicitly. Add new web-elements UI fixtures under packages/web-platform/web-elements/tests/fixtures and commit matching snapshots in packages/web-platform/web-elements/tests/web-elements.spec.ts-snapshots. + +--- +applyTo: "packages/react/runtime/src/**/*" +--- + +The delayed event queue exported from packages/react/runtime/src/lifecycle/event/delayEvents.ts is lazily initialized and may be undefined until the first delayed publish; when draining it, read it into a local variable, guard for undefined, and then clear that same array instance. diff --git a/packages/react/runtime/src/lynx/tt.ts b/packages/react/runtime/src/lynx/tt.ts index 03aec47cb7..936e4d51fa 100644 --- a/packages/react/runtime/src/lynx/tt.ts +++ b/packages/react/runtime/src/lynx/tt.ts @@ -36,7 +36,7 @@ function injectTt(): void { const tt = lynxCoreInject.tt; tt.OnLifecycleEvent = onLifecycleEvent; tt.publishEvent = delayedPublishEvent; - tt.publicComponentEvent = delayedPublicComponentEvent; + tt.publicComponentEvent = (_componentId, handlerName, data) => tt.publishEvent(handlerName, data); tt.callDestroyLifetimeFun = () => { removeCtxNotFoundEventListener(); destroyWorklet(); @@ -51,10 +51,9 @@ function injectTt(): void { } function onLifecycleEvent([type, data]: [LifecycleConstant, unknown]) { - const hasRootRendered = CHILDREN in __root; // never called `render(, __root)` // happens if user call `root.render()` async - if (!hasRootRendered) { + if (!(CHILDREN in __root)) { delayLifecycleEvent(type, data); return; } @@ -130,23 +129,18 @@ function onLifecycleEventImpl(type: LifecycleConstant, data: unknown): void { // TODO: It seems `delayedEvents` and `delayedLifecycleEvents` should be merged into one array to ensure the proper order of events. flushDelayedLifecycleEvents(); - if (delayedEvents) { - delayedEvents.forEach((args) => { - const [handlerName, data] = args; + const queuedDelayedEvents = delayedEvents; + if (queuedDelayedEvents) { + for (const [handlerName, data] of queuedDelayedEvents) { // eslint-disable-next-line prefer-const let [idStr, ...rest] = handlerName.split(':'); while (jsReadyEventIdSwap[idStr!]) idStr = jsReadyEventIdSwap[idStr!]?.toString(); - try { - publishEvent([idStr, ...rest].join(':'), data); - } catch (e) { - lynx.reportError(e as Error); - } - }); - delayedEvents.length = 0; + publishEvent([idStr, ...rest].join(':'), data); + } + queuedDelayedEvents.length = 0; } lynxCoreInject.tt.publishEvent = publishEvent; - lynxCoreInject.tt.publicComponentEvent = publicComponentEvent; // console.debug("********** After hydration:"); // printSnapshotInstance(__root as BackgroundSnapshotInstance); @@ -193,12 +187,10 @@ function flushDelayedLifecycleEvents(): void { // avoid stackoverflow if (flushingDelayedLifecycleEvents) return; flushingDelayedLifecycleEvents = true; - if (delayedLifecycleEvents) { - delayedLifecycleEvents.forEach((e) => { - onLifecycleEvent(e); - }); - delayedLifecycleEvents.length = 0; + for (const e of delayedLifecycleEvents) { + onLifecycleEvent(e); } + delayedLifecycleEvents.length = 0; flushingDelayedLifecycleEvents = false; } @@ -254,14 +246,6 @@ function publishEvent(handlerName: string, data: EventDataType) { } } -function publicComponentEvent(_componentId: string, handlerName: string, data: EventDataType) { - publishEvent(handlerName, data); -} - -function delayedPublicComponentEvent(_componentId: string, handlerName: string, data: EventDataType) { - delayedPublishEvent(handlerName, data); -} - function updateGlobalProps(newData: Record): void { Object.assign(lynx.__globalProps, newData);