Skip to content
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

iOS: UI will be blocked when show Alert while closing Modal #10471

Closed
nihgwu opened this issue Oct 20, 2016 · 184 comments
Closed

iOS: UI will be blocked when show Alert while closing Modal #10471

nihgwu opened this issue Oct 20, 2016 · 184 comments
Labels
Bug Platform: iOS iOS applications. Resolution: Locked This issue was locked by the bot.

Comments

@nihgwu
Copy link
Contributor

nihgwu commented Oct 20, 2016

When show Alert while closing Modal, the Alert dialog will disappear and the Modal will block the UI entirely even after reload, only on iOS.

'use strict';

import React, { Component } from 'react';

import {
  StyleSheet,
  View,
  Text,
  Modal,
  Alert,
} from 'react-native';

class demo extends Component {
  state = {
    showModal: false,
  }

  onShowModal = () => {
    this.setState({ showModal: true });
  }
  onCloseModal1 = () => {
    this.setState({ showModal: false }, () => {
      Alert.alert('Alert', 'UI will be blocked by the modal');
    });
  }
  onCloseModal2 = () => {
    this.setState({ showModal: false }, () => {
      setTimeout(() => {
        Alert.alert('Alert', 'Alert won\'t show');
      }, 200);
    });
  }
  onCloseModal3 = () => {
    this.setState({ showModal: false }, () => {
      setTimeout(() => {
        Alert.alert('Alert', 'Works fine');
      }, 510);
    });
  }
  render() {
    const { showModal } = this.state;
    return (
      <View style={styles.container}>
        <Text onPress={this.onShowModal}>Show modal</Text>
        <Modal animationType='slide' visible={showModal} onRequestClose={this.onCloseModal3} >
          <View style={styles.container}>
            <Text onPress={this.onCloseModal1}>Close modal immediately</Text>
            <Text onPress={this.onCloseModal2}>Close modal after 200ms</Text>
            <Text onPress={this.onCloseModal3}>Close modal after more then 500ms</Text>
          </View>
        </Modal>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'space-around',
  },
});


export default demo;
@nihgwu nihgwu changed the title UI is blocked when show Alert while closing Modal UI will be blocked when show Alert while closing Modal Oct 20, 2016
@leeight
Copy link
Contributor

leeight commented Oct 20, 2016

+1

@alma-socar
Copy link

alma-socar commented Oct 20, 2016

InteractionManager.runAfterInteractions also doesn't work in this case.

This is really frustrating.

@ganmor
Copy link

ganmor commented Oct 20, 2016

The opposite is also true, showing an alert in the same loop just before showing a modal will prevent the modal from being displayed

@nihgwu
Copy link
Contributor Author

nihgwu commented Oct 20, 2016

Yes, I guess they are the some issue, so I just make it simple to demonstrate

? 2016?10?20??20:07?Morgan Laupies <notifications@github.commailto:notifications@github.com> ???

The opposite is also true, showing an alert in the same loop just before showing a modal will prevent the modal from being displayed

You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHubhttps://github.com//issues/10471#issuecomment-255087481, or mute the threadhttps://github.com/notifications/unsubscribe-auth/ACeY8qW8mq4UpMUPScI2sI6Uhu6dFTOlks5q11mNgaJpZM4Kbxlp.

@K-Leon
Copy link
Contributor

K-Leon commented Oct 21, 2016

I noticed same issue with experimental navigator. An Error while transitioning kills the animation and freezes UI.

@ericvicenti ericvicenti changed the title UI will be blocked when show Alert while closing Modal iOS: <Modal> should inform InteractionManager while animating Oct 24, 2016
@ericvicenti
Copy link
Contributor

Looks like we need <Modal> animations to tie into InteractionManager. A PR would be appreciated for this!

Any issues with NavigationExperimental should be filed separately.

@nihgwu
Copy link
Contributor Author

nihgwu commented Oct 25, 2016

@ericvicenti I didn't use NE or Navigator in demo at all, and I don't think this issue is related to InteractionManager, my guess is that Modal and Alert are using the same controller or view, so there will be a conflict when trying to control them simultaneously, I know nothing about iOS or I would be glad to make a PR, because it's really annoying but has a quite common use case

In my demo, I've show the different results when waiting for a certain milliseconds. I guess the Modal need 500ms to dismiss, so it's safe to show the Alert after 500ms. Perhaps you are right to tie Modal animations into InteractionManager, but what if I show the Alert first as @ganmor mentioned above, I can't close the Alert manually, or if I could, would there be the same occasion that we need to wait for the InteractionManager before showing the Modal?

Would someone expert in iOS take a look at this issue?

@nihgwu nihgwu changed the title iOS: <Modal> should inform InteractionManager while animating iOS: UI will be blocked when show Alert while closing Modal Nov 4, 2016
@nihgwu
Copy link
Contributor Author

nihgwu commented Nov 4, 2016

ping @grabbou

facebook-github-bot pushed a commit that referenced this issue Nov 5, 2016
Summary:
further discussion: should there be a `onClose` or `onClosed` to pair with `onShow`? which would make a workaround for #10471 much easier
Closes #10669

Differential Revision: D4133832

Pulled By: hramos

fbshipit-source-id: 644a5bb6b9da697c81fc96ae4da196ba5b4050cb
@DevBkIL
Copy link

DevBkIL commented Nov 7, 2016

do someone have a fix for it?

@jasonmerino
Copy link

@DevBkIL my not great solution is to set a timeout, enough for the presented view controller to be the one under the Modal, and then present the Alert. Like I said, not great, but it's working until this can be properly resolved.

mlguys pushed a commit to mlguys/react-native that referenced this issue Nov 8, 2016
Summary:
further discussion: should there be a `onClose` or `onClosed` to pair with `onShow`? which would make a workaround for facebook#10471 much easier
Closes facebook#10669

Differential Revision: D4133832

Pulled By: hramos

fbshipit-source-id: 644a5bb6b9da697c81fc96ae4da196ba5b4050cb
@nihgwu
Copy link
Contributor Author

nihgwu commented Nov 12, 2016

UPDATE: UI will be blocked when show Share/ActionSheet while closing Modal too
And if we are showing the Alert/Share/ActionSheet, then show a Modal, the UI will be froze too
ping @javache @mkonicek

@joegoodall1
Copy link

Any update/resolution to this? We're still having problems with this a month after this issue was first reported.

@mjamiesoncs
Copy link

+1

@sibelius
Copy link

I can confirm this using https://github.com/jaysoo/react-native-prompt

any workaround?

@baurine
Copy link

baurine commented Nov 23, 2016

today I met the similar problem after I upgraded the react-native from 0.33 to 0.37. I want to show an Alert dialog after close the Modal, but Modal doesn't disappear, even after I close the Alert dialog and use cmd + R to reload the app. only in iOS, and it works fine by react-native 0.33.

the code likes following:

  renderModal() {
    return (
      <Modal
        animationType = 'fade'
        transparent={true}
        visible={this.state.isProcessing}
        onRequestClose={()=>{}}>
        <View style={styles.modalContainer}>
          <LoadingSpiner size='large' color='white' styleAttr='Normal'/>
        </View>
      </Modal>
    )
  }

  _pressNext() {
    // display a Modal with a spinner
    this.setState({isProcessing: true}}

    // network request
    // ...
  }

  componentWillReceiveProps(nextProps) {
      // ...

      // to hide the Modal with a spinner
      this.setState({isProcessing: false})
      Alert.alert('title', 'Something has done!', [
        { text: 'Got it', onPress: () => {} }
      ])
    }
  }

then I try to use setTimeout to work around it, the code likes following:

  componentWillReceiveProps(nextProps) {
      // ...

      // to hide the Modal with a spinner
      this.setState({isProcessing: false})
      setTimeout( () => {
        // this log will output
        console.log("show alert")
        // but Alert doesn't display
        // sometimes it will display occasionally
        Alert.alert("title", "msg")   
      }, 200)
  }

then the Modal will disappear, but, the Alert dialog can't display!!!

I also tried run setTimeout in setState callback, like this:

  this.setState({isProcessing: false}, () => {
    setTimeout( () => {
      Alert.alert("title", "msg")
    }, 200)
  }

but the same result, Alert dialog doesn't pop up yet.

finally, I decide to hide Modal after I close the Alert dialog, and that works! code likes following:

Alert.alert("title", "msg", [
  { text: "OK", onPress: () => { this.setState({ isProcessing: false } }    
])

@vicmpen
Copy link

vicmpen commented Nov 23, 2016

@baurine
Had the same problem with RN 35, a little higher timeout worked for me (600), but ultimately i also did what you did, hide the modal on OK Pressed in Alert

return Alert.alert('Σφάλμα','Η υπηρεσία δεν είναι διαθέσιμη αυτή τη στιγμή. Παρακαλούμε δοκιμάστε σε λίγο.', [ {text: 'OK', onPress: () =>this.setState ({spinnerIsVisible:false})} ])

@b8ne
Copy link

b8ne commented Nov 29, 2016

We are having a similar issue in niftylettuce/react-native-loading-spinner-overlay. Could it be that this is a more general issue relating to UI updating on state change rather than just a modal issue?

@ericvicenti
Copy link
Contributor

cc @javache

@AlimovSV
Copy link

AlimovSV commented Dec 1, 2016

+1

1 similar comment
@mattotodd
Copy link

+1

@susan-github
Copy link

+1, anyone has found solutions?

ElyDev added a commit to ElyDev/YungDevz that referenced this issue Jul 16, 2019
Alert.alert cancels Modal dismiss even so we end up with a non responsive Modal confirmation dialog. This is explained here:
facebook/react-native#10471

I used the workaround suggested in the thread by adding a short timeout to Alert.alert

Todo: eventually we should better handle this by launching the error dialog from the teacher screen instead of redux, but this workaround is good for now.
@deepas96
Copy link

deepas96 commented Jul 16, 2019

Still having this issue. I am showing a loader on screen which is a Modal and then I need to show popup after API response. It works on Android to hide loader on Ok button action of Alert popup, but not working on iOS. Loader gets stuck on screen and popup never appears.
Getting this error on Xcode :
Warning: Attempt to present <RCTModalHostViewController: 0x7fda01a0d970> on <UIViewController: 0x7fd9fe447fd0> which is already presenting <RCTModalHostViewController: 0x7fda01a0d2d0>
Need to kill the app and restart it. Can anyone have solution?

@deepas96
Copy link

oh, sorry for my misunderstanding... Unfortunately I wasn't able to test on master branch because ios build failed for me:(
For now I fixed the issue with work-around by hiding modal on Alert OK press. Anyway thanks a lot!

It worked on android but not on iOS. Please suggest some solution.

@zhongwuzw
Copy link
Contributor

Hi guys, I think we may fixed it on latest master branch, if you guys tried it which not worked, please file a new issue.

@TechSatya
Copy link

Using of "AlertIOS" component for iOS helped me to avoid this issue.

The workaround is as below;

AlertIOS.alert(
  'Alert Title', 'Alert message',
    [
      {
        text: 'OK', onPress: () => this.setState({ loading: false})
      }
    ]
);

@cristianoccazinsp
Copy link
Contributor

@TechSatya are you confident about this? One would guess that Alert uses AlertIOS behind the scenes.

@TechSatya
Copy link

Yes, @cristianoccazinsp I think this is the only possible solution as of now because the other availed solution didn't help me in any way. As from the last 2 days I was stuck in this issue, I started giving a try to the "AlertIOS" component and fortunately found it successful.

Yes, Alerts uses AlertIOS behind it, but don't know how it got successful? Maybe something lacks in implementing of AlertIOS in Alert behind the scenes.

To be more specific to the code for both platforms;

(Platform.OS === 'android' ? Alert : AlertIOS).alert(
  'Alert Title', 'Alert message',
    [
      {
        text: 'OK', onPress: () => this.setState({ loading: false})
      }
    ]
);

@fengyouchao
Copy link

fengyouchao commented Jul 22, 2019

Using setTimeout is NOT a safe way to avoid this problem. I recommend using onDismiss props in iOS to do something after the modal closed (including Alert some messages). I created a class named ModalView to replace RN Modal class.

class ModalView extends React.Component {

    state = {
        visible: false
    };

    close({then} = {}) {
        if (Platform.OS === 'ios') {
            this.setState({visible: false, onDismiss: then});
        } else {
            this.setState({visible: false});
            if (then !== undefined) {
                then();
            }
        }
    }

    show() {
        this.setState({visible: true});
    }

    render() {
        return (
            <Modal
                visible={this.state.visible}
                onDismiss={this.state.onDismiss}
                {...this.props}>
                {this.props.children}
            </Modal>
        )
    }
}

Using

Just replace your Modal with ModalView and add ref props.

<ModalView ref={ref=>this.modalView=ref} 
   //other Modal props
>
//your components
<ModalView>

Show Modal

this.modalView.show();

Close Modal

this.modalView.close();

Alert some messages after the modal closed.

this.modalView.close({
      then: () => {
          Alert.alert('Hello World');
          //your code
      }
});

@hinodi
Copy link

hinodi commented Aug 4, 2019

I have same issue here when i use "react-native": "0.59.9" and "react-native-modal": "^11.3.1"

And my solution is show the alert in Modal onDismiss props

@giautm
Copy link

giautm commented Sep 9, 2019

Agree with @fengyouchao, your solution was working well. Thank you.

@hoanglm4
Copy link

hoanglm4 commented Dec 5, 2019

I think facebook engineering need fix this bug.

@adimshev
Copy link

adimshev commented Dec 8, 2019

You can use my solution (prevent opening Modal when Alert is opened, and open it after Alert was closed, using redux and hooks).

My solution is not full, later I will send solution that prevents opening many Modals at same time and prevents opening Alert while Modal is closing (based on queue and store subscribe).

In any case, you can refine my idea.

Modal + Alert.zip

@teledemic
Copy link

FWIW a modification to @fengyouchao's workaround if you are using async/await is

async Close() {
	return new Promise((resolve, _reject) => {
		this.setState({ visible: false, onDismiss: resolve });
	});
}

Then you can just await this.modalView.Close();

@jbenzshawel
Copy link

Still an issue with RN 0.61.2 - does anyone if this is fixed in any later 0.61 patch releases?

@yogesh1490
Copy link

When show Alert while closing Modal, the Alert dialog will disappear and the Modal will block the UI entirely even after reload, only on iOS.

'use strict';

import React, { Component } from 'react';

import {
  StyleSheet,
  View,
  Text,
  Modal,
  Alert,
} from 'react-native';

class demo extends Component {
  state = {
    showModal: false,
  }

  onShowModal = () => {
    this.setState({ showModal: true });
  }
  onCloseModal1 = () => {
    this.setState({ showModal: false }, () => {
      Alert.alert('Alert', 'UI will be blocked by the modal');
    });
  }
  onCloseModal2 = () => {
    this.setState({ showModal: false }, () => {
      setTimeout(() => {
        Alert.alert('Alert', 'Alert won\'t show');
      }, 200);
    });
  }
  onCloseModal3 = () => {
    this.setState({ showModal: false }, () => {
      setTimeout(() => {
        Alert.alert('Alert', 'Works fine');
      }, 510);
    });
  }
  render() {
    const { showModal } = this.state;
    return (
      <View style={styles.container}>
        <Text onPress={this.onShowModal}>Show modal</Text>
        <Modal animationType='slide' visible={showModal} onRequestClose={this.onCloseModal3} >
          <View style={styles.container}>
            <Text onPress={this.onCloseModal1}>Close modal immediately</Text>
            <Text onPress={this.onCloseModal2}>Close modal after 200ms</Text>
            <Text onPress={this.onCloseModal3}>Close modal after more then 500ms</Text>
          </View>
        </Modal>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'space-around',
  },
});


export default demo;

I have same issue.
Has anyone done this using hooks as useState in functional component ?

@revich2
Copy link

revich2 commented Feb 6, 2020

@yogesh1490

I have a similar problem, but the alert may appear at an undetermined point in time and is not logically dependent on Modal component.

My workaround looks like this:

  const OldAlert = Alert.alert;

  Alert.alert = (...args) => {
    requestAnimationFrame(() => {
      requestAnimationFrame(() => {
        OldAlert(...args);
      });
    });
  };

This is “magic,” but it works for me.

@cristianoccazinsp
Copy link
Contributor

@revich2 interesting... Why do you need 2 requestAnimationFrame ? Would it be the same with runAfterInteractions ?

@react-native-bot can we re open this?

@revich2
Copy link

revich2 commented Feb 7, 2020

@cristianoccazinsp The first thing I used was InteractionManager.runAfterInteractions, but for some reason, this did not work for me, so I had to make 2 requestAnimationFrame to defer the call to Alert.alert to the next iteration in the event loop.

@phamlap228
Copy link

in my case im change others Modal and resolved :D

@thanhluantl2304
Copy link

I still face that problem when close one modal and open other modal immediately. My current solution is using setTimeout but that's inconvenient. Have Facebook staff fixed it yet? Or someone has another solution?

@mangBert
Copy link

Same here. any update on this?

@scarlac
Copy link
Contributor

scarlac commented May 28, 2020

Cause:
Rendering a modal is a polite request. It does not guarantee it will show. The root cause is that the native iOS implementation assumes it's a guarantee.
This is a native bug that needs to be fixed in the Modal component on iOS. The root cause is that modals are imperative in iOS and you may request to open one, but it may not succeed. iOS implementation does not consider that the modal potentially never opens, so it still triggers the underlying lock-up mechanism. This leaves the app half locked up with no modal.

Related bugs:
This bug will also happen if you try to use pageSheet presentation style and user swipes down on the sheet. This user action causes the modal to disappear, yet React Native thinks it's still visible, leaving your app in a locked state. Same cause.

Workaround:
If you are okay with the modal not showing but want to fix the lock up, wrap your <Modal> in a View with 0 height, like this:

function BrokenModal() {
  alert('this alert will show, modal wont');
  return (
    <View style={{height: 0}}>
      <Modal animated>
        <Text>This text will never appear</Text>
      </Modal>
    </View>
  );
}

@facebook facebook locked as resolved and limited conversation to collaborators Jun 6, 2020
@react-native-bot react-native-bot added the Resolution: Locked This issue was locked by the bot. label Jun 6, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug Platform: iOS iOS applications. Resolution: Locked This issue was locked by the bot.
Projects
None yet
Development

No branches or pull requests