[RN-0.72.4]-[IOS]-Modal make iOS freeze app when modal closes
Description
I using react native version 0.72.4. Open a modal then close it make app freeze on both actual iOS device and simulator, there is an issue when we close the modal, the whole app freezes. I looked at the code in RCTModalHostView, and the method named dismissModalViewController, seemed to be at fault, because it wasn't dismissing the view controller from the main thread, if I am not wrong. So I called the dismissal method from the main queue and voila, the app don't freeze anymore.
it's working as expected
Other members are also facing this issue.
React Native Version
0.72.4
Output of npx react-native info
System: OS: macOS 12.6 CPU: (12) x64 Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz Memory: 1.78 GB / 16.00 GB Shell: version: 5.8.1 path: /bin/zsh Binaries: Node: version: 16.20.0 path: ~/.nvm/versions/node/v16.20.0/bin/node Yarn: version: 1.22.19 path: /usr/local/bin/yarn npm: version: 8.19.4 path: ~/.nvm/versions/node/v16.20.0/bin/npm Watchman: version: 2023.02.13.00 path: /usr/local/bin/watchman Managers: CocoaPods: version: 1.11.3 path: /usr/local/bin/pod SDKs: iOS SDK: Platforms: - DriverKit 22.2 - iOS 16.2 - macOS 13.1 - tvOS 16.1 - watchOS 9.1 Android SDK: Not Found IDEs: Android Studio: 2021.2 AI-212.5712.43.2112.8609683 Xcode: version: 14.2/14C18 path: /usr/bin/xcodebuild Languages: Java: version: 11.0.15 path: /usr/bin/javac Ruby: version: 2.6.8 path: /usr/bin/ruby npmPackages: "@react-native-community/cli": Not Found react: installed: 18.2.0 wanted: 18.2.0 react-native: installed: 0.72.4 wanted: 0.72.4 react-native-macos: Not Found npmGlobalPackages: "react-native": Not Found Android: hermesEnabled: false newArchEnabled: false iOS: hermesEnabled: true newArchEnabled: false
Steps to reproduce
Open a modal then close it make app freeze on both actual iOS device and simulator, sometime can tap to view inside modal, sometime not, app is freeze. Also when modal was closed, it still make app freeze
if visible = true then show modal Ok, but you when click on props.onClose() then freeze app.
const ModalTest = (props) => {
return (
<Modal
animationType="slide"
transparent={true}
visible={props.isVisible}>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<Text style={styles.modalText}>Hello World!</Text>
<Pressable
style={[styles.button, styles.buttonClose]}
onPress={props.onClose}>
<Text style={styles.textStyle}>Hide Modal</Text>
</Pressable>
</View>
</View>
</Modal>
);
};
I try edit code in file /React/Views/RCTModalHostView.m/dismissModalViewController then working as expected
add dispatch_async(dispatch_get_main_queue(), ^{ ..... });
help me, please
Snack, screenshot, or link to a repository
No response
| :warning: | Missing Reproducible Example |
|---|---|
| :information_source: | We could not detect a reproducible example in your issue report. Please provide either:
|
Same issue for me! Please give me help... @Kudo
@NickGerleman please help me 🙏
Same issue.
Same issue. Any progress?
@GuoXiaoyang, I upgraded to RN 0.73.2 but still have this issue .
Any progress? This is really bad for our app :(
i'm also facing this, any patch or workaround?
i'm also facing this, any patch or workaround?
I hacked the Dialog to make sure only one is being in animation state:
import React, { createContext, ReactNode, useContext } from "react";
interface DialogInstance {
id: string;
setIsVisible: (visible: boolean) => void;
}
interface DialogInstanceInternal extends DialogInstance {
isClosing: boolean;
}
interface IDialogManager {
get: (instance: DialogInstance) => DialogInstanceInternal | undefined;
queue: DialogInstanceInternal[];
hideInstance: (instance: DialogInstance) => void;
pushInstance: (instance: DialogInstance) => void;
removeInstance: (instance: DialogInstance) => void;
updateInstances: () => void;
}
const DialogContext = createContext<IDialogManager | undefined>(undefined);
// Fix for
// https://github.com/facebook/react-native/issues/32504#issuecomment-1024642375
// https://github.com/facebook/react-native/issues/32329
// Basically we need to make sure not to set "isVisible" to true in Dialog if there's another dialog in closing animation.
const DialogManager: IDialogManager = {
queue: [],
pushInstance(instance) {
if (this.get(instance)) {
return;
}
this.queue.push({
...instance,
isClosing: false,
});
this.updateInstances();
},
get(instance) {
return this.queue.find((item) => item.id === instance.id);
},
removeInstance(instance) {
this.hideInstance(instance);
this.queue = this.queue.filter((item) => item.id !== instance.id);
this.updateInstances();
},
hideInstance(instance) {
const instanceToHide = this.get(instance);
if (!instanceToHide) {
return;
}
instanceToHide.setIsVisible(false);
instanceToHide.isClosing = true;
},
updateInstances() {
// We can't show another dialog if there is one already in closing animation state
if (this.queue.length === 0 || this.queue.some((instance) => instance.isClosing)) {
return;
}
const instanceAtTop = this.queue[this.queue.length - 1];
instanceAtTop.isClosing = false;
instanceAtTop.setIsVisible(true);
},
};
export const useDialogManager = (): IDialogManager => {
const context = useContext(DialogContext);
if (!context) {
throw new Error("useDialog must be used within a DialogProvider");
}
return context;
};
export const DialogProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
return <DialogContext.Provider value={DialogManager}>{children}</DialogContext.Provider>;
};
export const Dialog = ({
visible,
...props
}: Props): ReactElement | null => {
const id = useId();
const dialogManager = useDialogManager();
const [isVisible, setIsVisible] = useState(visible);
const instance = {
id,
setIsVisible,
};
useEffect(() => {
visible ? dialogManager.pushInstance(instance) : dialogManager.hideInstance(instance);
}, [visible, dialogManager]);
useEffect(() => {
return () => {
dialogManager.removeInstance(instance);
};
}, []);
const combinedOnModalDismissed = (props: any) => {
dialogManager.removeInstance(instance);
if (onDialogDismissed) {
onDialogDismissed(props);
}
};
if (!dialogManager.get(instance)) {
return null;
}
return (
<UiDialog
visible={isVisible}
onDialogDismissed={combinedOnModalDismissed}
{...props}
>
{children}
</UiDialog>
);
};
- UiDialog is the dialog from react-native-ui-lib
- You need to wrap the app to
<DialogProvider></DialogProvider>
This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.
This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.