-
-
Notifications
You must be signed in to change notification settings - Fork 530
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(Paper,iOS): dismiss all attached view controllers correctly on reload #2175
Conversation
Thank you! The code changes look good, however I'll need to test it & see what's going on on Fabric. Will be back with more insights once I do that (most likely Monday / Tuesday). |
react-native-screens/ios/RNSScreenStack.mm Lines 696 to 704 in 5cfafbb
EDIT: I removed couple of messages, sorry. It was a mistake |
@kkafar friendly reminder :) |
Yeah, thanks for the reminder. I'm convinced that GitHub needs a option of posting a periodic notification until something is done. I'll allow myself liberty of pushing onto your branch, hope you don't mind. |
Ok, I'm thinking through this change right now & I see a potential issue. This goes through system chain of Second remark is that is should be sufficient to pop only bottom-most modal to dismiss whole chain, w/o the need for recursion, but that's implementation detail I'll correct later. Right know I want to ask you if you have throughput to provide me (& the PR) with issue reproduction so that I can play with it right now & someone has opportunity to test this behaviour later. |
This PR aims to fix issues with reloading (pressing R) when RNS is used in conjunction with third-party UIViewControllers. We have a pretty complex setup, and I did not encounter any regressions with my proposed fix. Your described scenario is exactly one of the issues this PR solves because it closes the modals. I’d suggest we merge this and address any regressions if they are reported (which I highly doubt). I also tested scenarios where you navigate from an open modal to a different bottom tab, and everything behaved exactly the same with the change.
I don’t think this is a thing in iOS. Modals never really belong to a specific stack; they are always pushed on top (they can have an inline stack, though, which is unrelated in my opinion) |
I'll insist on proper reproduction here, so that I can play around & test this. I'll have some throughput to prepare one myself most likely early next week.
You are right with respect to the native platform. I meant by "modals owned by stack" these screens, that have modal presentation and are rendered directly as children of given |
I'll create a repro for you. |
While working on your repro, I discovered that my PR fixes all related modal problems on reload, not just those by third parties. Currently, RNS closes all modals but keeps the last one mounted. This also makes the yellowbox log unclickable until all modals are closed. Little side bug I've discovered: when pulling on modals the header height gets totally borked. It's also visible in my video, but maybe something for a different PR. Repro coming in 10min. issue.mp4 |
Hey @kkafar, here is the link to Repro; https://github.com/hirbod/RNSModalDismissRepro Make sure to check the (i), tab one, and tab two. The video makes things clearer in my opinion. Whenever you see the "Metro reloading" bar, I pressed R. full-repro.mp4Once you apply my PR, all the issues are gone, reloading will always close the modals, and navigating from a modal to a screen will correctly close all open modals and not leave anything open which breaks touch inputs. (Both RNS and third party) |
Hi @kkafar, did you get a chance to look into this? 😊 |
No, haven't sit down to yet, sorry. I'll put it much higher on my board rn |
any update on this one? 👀 |
@tboba any chance you could look into this? |
I found out that it works when setting import { useState, useEffect, useMemo, ComponentProps } from 'react';
import Animated, {
useSharedValue,
useAnimatedReaction,
runOnJS,
FadeOut,
} from 'react-native-reanimated';
const useModalVisibilityState = (isVisibleProp: boolean | undefined) => {
const isSharedVisible = useSharedValue(isVisibleProp);
const [isModalVisible, setIsModalVisible] = useState(isVisibleProp);
useEffect(() => {
if (isVisibleProp) {
isSharedVisible.value = true;
}
}, [isSharedVisible, isVisibleProp]);
useAnimatedReaction(
() => isSharedVisible.value,
(curr) => runOnJS(setIsModalVisible)(curr),
);
const fadeOutAnimation = useMemo<
Exclude<ComponentProps<typeof Animated.View>['exiting'], undefined>
>(
() =>
FadeOut.withCallback((finished) => {
if (finished) {
isSharedVisible.value = false;
}
}),
[isSharedVisible],
);
return useMemo(
() => ({
isModalVisible,
fadeOutAnimation,
}),
[fadeOutAnimation, isModalVisible],
);
};
export default useModalVisibilityState; then set the modal's animation prop to |
@lucacaputo not sure why you are hijacking this PR and what your snippet aims to solve? |
@hirbod sorry I thought I was commenting on a related issue |
This change aligns more with the current behaviour
Hey, I finally have time to look into it. I'm sorry for such long delay. I've rebased the PR on main and tested the issue on a added reproduction (Test2175 in example apps). I can't reproduce the issue. I don't want to use Expo-based reproduction, because expo often runs its own logic which might interfere, thus I've prepared one (see files of this PR) based solely on react-navigation@v7 attempting to mimic structure you presented. The example has stack navigator, with few screens: first contains tab navigator, the rest of them are push screen, transparent modal screen & modal screen. Inside tab navigator there are buttons to open the modals from outer stack navigator. I can't trigger the behaviour you described in any navigation scenario. Might it be possible, that this issue has been fixed some other way? We had merged few PRs related to foreign modals behaviour last few weeks. Caution I've crudely force-pushed after rebase, consider pulling to a new local branch. |
I see also high chance that I've messed up the reproduction, lemme know when you have a moment |
@kkafar Current versions still break. We're using Expo Router, and my reproduction is still working. Upgrading to v7 is not an option for us right now. My PR fixes the issue. Honestly, it's tough to look into this again after almost 4 months. |
I see, but I'm not willing to merge this, since I'm not able to reproduce the issue without Expo involved. I'll ask @adrianryt to take a look with fresh pair of eyes. |
Expo Router is one of the most significant use cases for react-native-screens, and having Expo involved is a big deal. |
I think the current reproduction provided by @kkafar is not correct. I was able to recreate the bug without expo - with the use of Kacper's test. I will get back to this tomorrow and investigate it further. |
I allowed myself to push changes to test file. I want to note 4 scenarios related to the issue: Scenario 1Repro:
Screen.Recording.2024-09-27.at.13.35.35.movSummary: Scenario 2Repro:
Video: Screen.Recording.2024-09-27.at.13.39.37.movSummary: Scenario 3Repro:
Screen.Recording.2024-09-27.at.13.46.15.movSummary: Scenario 4Repro:
Screen.Recording.2024-09-27.at.13.53.09.movSummary: Conclusions
cc @kkafar |
Thank you @adrianryt! I'll work on the implementation suggestion (link) and proceed with merging. |
How is the status here? |
Thanks for reminder. I've adjusted the implementation & tested it. It turned out we missed a message to All test cases described earlier by @adrianryt seem to work & all the cases I've came up with also work now. What's left is:
I'm on it right now |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Took a long time - sorry for that.
I've changed the implementation - instead of implementing recursive dismissal by hand we now dismiss all controllers presented from _controller
or "down" the presented view controllers hierarchy by letting system do its job. With these changes view controllers should be removed immediately on reload, w/o glitchy effect reported by @adrianryt.
If a controller was presented outside the screen stack (foreign modal not inside a screen) it should be responsible for its own dismissal.
Description
This PR addresses an issue with react-native-screens where modals were not being dismissed correctly during app reloads when combined with foreign view controllers. The fix involves enhancing the invalidate method to recursively dismiss all presented view controllers, ensuring a clean state on reload.
This is not a development-only problem; this fix also addresses reloads from OTA updates.
Changes
Screenshots / GIFs
before.mp4
after.mp4
The red background is from a
transparentModal
by RNS, the sheet is a foreign view controller. Before the change,react-native-screens
would break on reload if a foreign view controller was mounted on top. I came to the solution after finding this PR. The issue originally started here. After my changes, RNS works correctly on reload with third-party controllers.I have no experience with Fabric, so I can't help with that. Feel free to update the solution for Fabric if needed.
Test code and steps to reproduce
Test2175
Checklist