Repository navigation
Conversation
A #[Lazy] screen's placeholder is published before mount() runs, so native pushes it straight away and the user can pop it while mount() is still busy (a network call, say). Native sends a system back, but it sits in the event queue until mount() returns. The router then calls flushDeferredTransition(), and its nativephp_element_reset() empties the queue before the screen's first real frame. The back was thrown away, PHP stayed on the screen the user had left, and that frame pushed the screen back on. On the simulator the chat just slides back in after the user has left it. The device report in #471 (list under the chat's nav bar, taps do nothing) is most likely the same drift, with the re-push landing while the pop is still animating. flushDeferredTransition() now drains the queue before the reset and keeps the events that aren't bound to the outgoing screen: system back, native events, hot reload and shutdown. Taps and other callback events are still dropped, as before. The runloop handles held events before it waits for new ones. When a system back is held, the screen skips its first frame and back() skips its farewell frame, since either would push the screen back on. Android runs the same router and pops its path the same way before PHP answers, so it gets the fix too. Fixes #471 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
Member
Author
|
Closing as this is solving for problems with |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #471
What was wrong
A
#[Lazy]screen publishes its placeholder beforemount()runs (NativeRouter::loop(), line 494), so native pushes the screen straight away. If the user pops it with the back chevron or an edge swipe whilemount()is still busy, native sends a system back (type 8) and it waits in the event queue.When
mount()returns, the screen's first render callsflushDeferredTransition()(NativeComponent.php:2717), which callsnativephp_element_reset()(NativeRouter.php:133). The reset empties the whole event queue (event_queue_clear_locked()in the runtime'snphp_element.c), so the back is thrown away. The runtime even logs it:Element reset discarded 1 undelivered event(s).PHP then publishes the screen the user already left. The coordinator sees a URI that isn't on its path and pushes it again (NavigationCoordinator.swift:126). PHP now sits on the chat while the user has tried to leave it, and nothing ever corrects that, because the back PHP needed is gone. On the simulator the chat just slides back in. The device report (list under the chat's nav bar, dead taps) is most likely the same thing with the re-push landing mid-animation. I couldn't get that exact picture on the simulator.
What changed
flushDeferredTransition()now drains the queue just before the reset and keeps events that aren't bound to the outgoing screen: system back, native events, hot reload and shutdown. Taps and other callback events are still dropped, as before, so a tap meant for the old screen can't hit a callback on the new one.publishFinalState()skips the farewell frame. Either one would push the screen back on.Android uses the same router, and its
BackHandlerpops the path before PHP answers, same as iOS. So Android had the same lost back and gets the same fix. I checked that by reading the code, not on a device. The Android symptom would be the chat coming back rather than a stuck screen.Other open PRs
I applied each one to main and ran the same repro against it. None of them fix this.
handleSystemBack(). fix: skip the farewell frame when back() answers a system back #370 already conflicts with main. Once that's resolved, this PR merges on top cleanly and the full suite passes.background()returns frommount()straight away, and 0 of 10 backs were lost. A blockingmount()still lost 10 of 10. It already conflicts with main. Once that's resolved, this PR merges on top cleanly.What to watch
mount()are now delivered instead of dropped. That covers deep links, async task completions and#[On]events. I think that's what everyone expected already, but it is a behaviour change.nativephp_element_wait_event(0)until it returns null. On device and under Jump that returns straight away when the queue is empty.Testing
Test app: a list and a chat in one native stack, with the chat's
mount()blocking for 1.2s. There are three variants: blocking, 0.3s, and#[Lazy]with 1.2s. iPhone 17 Pro simulator, iOS 26.2.RunsAndroid.php.🤖 Generated with Claude Code