react-native icon indicating copy to clipboard operation
react-native copied to clipboard

[RN-0.72.4]-[IOS]-Modal make iOS freeze app when modal closes

Open TrungPQ15 opened this issue 2 years ago • 8 comments

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 Screen Shot 2023-08-16 at 16 34 08

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(), ^{ ..... }); image

help me, please

Snack, screenshot, or link to a repository

No response

TrungPQ15 avatar Aug 16 '23 09:08 TrungPQ15

:warning: Missing Reproducible Example
:information_source: We could not detect a reproducible example in your issue report. Please provide either:
  • If your bug is UI related: a Snack
  • If your bug is build/update related: use our Reproducer Template. A reproducer needs to be in a GitHub repository under your username.

github-actions[bot] avatar Aug 16 '23 09:08 github-actions[bot]

Same issue for me! Please give me help... @Kudo

HuyNguyen313 avatar Aug 16 '23 10:08 HuyNguyen313

@NickGerleman please help me 🙏

TrungPQ15 avatar Sep 03 '23 05:09 TrungPQ15

Same issue.

alexyazvinsky avatar Sep 12 '23 16:09 alexyazvinsky

Same issue. Any progress?

GuoXiaoyang avatar Jan 04 '24 05:01 GuoXiaoyang

@GuoXiaoyang, I upgraded to RN 0.73.2 but still have this issue .

TrungPQ15 avatar Jan 21 '24 11:01 TrungPQ15

Any progress? This is really bad for our app :(

genesiscz avatar Jan 22 '24 13:01 genesiscz

i'm also facing this, any patch or workaround?

janandaraj avatar Feb 07 '24 07:02 janandaraj

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>

genesiscz avatar Feb 12 '24 18:02 genesiscz

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.

react-native-bot avatar Aug 11 '24 05:08 react-native-bot

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.

react-native-bot avatar Aug 11 '24 05:08 react-native-bot