FisNaN
FisNaN

Reputation: 2875

How to await for the response of Alert dialog in React Native?

From my observation, the Alert dialog seems built on top of the React Native app. So it pops out everytime you call it, and doesn't to be in the render function.
The catch is it is not an async task so the code after Alert will continue to execute regardless the callback function.

The code below demonstrates a situation where the Alert dialog keeps popping out because it reads the same barcode over and over again.
(It is written in TypeScript. Just take my word, this is a valid snippet.)

 import * as React from "react";
 import Camera from "react-native-camera";
 import { Alert } from "react-native";

 export default class BarcodeScanSreen extends React.Component<any ,any> {
 private _camera;
 private _onBarCodeRead = e => {
    if (e.type === "QR_CODE") {
        Alert.alert(
            "QRCode detected",
            "Do you like to run the QRCode?",
            [
                { text: "No", onPress: this._onNoPress },
                { text: "Yes", onPress: this._onYesPress }
            ],
            { cancelable: false }
        );
    }
};

 private _onYesPress = () => { /* process the QRCode */ }

 private _onNoPress = () => { /* close the alert dialog. */ }

render() {
    return (
        <Camera
            onBarCodeRead={this._onBarCodeRead}
            aspect={Camera.constants.Aspect.fill}
            ref={ref => (this._camera = ref)}
        >
            {/* Some another somponents which on top of the camera preview... */}
        </Camera>
    );
}
}

Is there a way to pause the JS code and await the response from Alert?

Upvotes: 24

Views: 29308

Answers (7)

Venkatesh Paithireddy
Venkatesh Paithireddy

Reputation: 39

https://www.npmjs.com/package/react-native-async-alert

Use this npm. Which is same like the above answer of @Makatun, but in easy and reliable way.

  import {AlertProvider} from 'react-native-async-alert';
  import {useShowAlert} from 'react-native-async-alert';

  function App() {
    return (
      <AlertProvider>
        {/* Content */}
      </AlertProvider>
     );
  }

And simply use it like this. You can customize your alert also.

  import {useShowAlert} from 'react-native-async-alert';
  const result = await showAlert({
    title: 'Title',
    text: 'text',
  });
  console.log(result);

Upvotes: 0

Makatun
Makatun

Reputation: 1017

If you need to return a promise:

const asyncAlert = (title, message, callback) => (new Promise((resolve) => {
    Alert.alert(
        title,
        message,
        [{ text: "Cancel" }, { text: "OK", onPress: () => resolve() }], { cancelable: false }
    );
}).then(callback));

Usage:

return asyncAlert("My Title", "Are you sure?", () => SOME_PROMISE_FUNCTION())

Upvotes: 1

user2209008
user2209008

Reputation:

Here's a simple solution.

The trick used here is to make a function that calls the button's onPress function, then resolve the promise with the index of the button. Note that this requires the alert to be uncancellable.

showAsyncAlert = (title, message, buttons, options) => {
  return new Promise((resolve, reject) => {
    // We can't detect a cancellation, so make sure the Alert is not cancellable.
    options.cancellable = false
    buttons.forEach((button, index) => {
      let onPress = button.onPress
      button.onPress = option => {
        if (onPress) {
          onPress(option)
        }
        resolve(index)
      }
    })
    Alert.alert(title, message, buttons, options)
  })
}

Usage:

let option = await showAsyncAlert(title, message, buttons options)

if (option === 0) {
    foo()
} else {
    bar()
}

Upvotes: 4

evil_morty
evil_morty

Reputation: 21

I have some workaround for this,If you have alert function like below

     Alert.alert(
                'Delete comment?',
                'Are you sure you want to delete this comment?',
                [
                    {
                        text: 'Cancel',
                        onPress: () => console.log('Cancel Pressed'),
                        style: 'cancel',
                    },
                    { text: 'yes', onPress:() => this.props.deleteComment(commentId),
                ],
                { cancelable: false },
            );
  //call after comment is deleted
  refreshPage();

This code does not wait for alert's response, it will execute refreshPage() immediately.

so, you can do something like

         Alert.alert(
                    'Delete comment?',
                    'Are you sure you want to delete this comment?',
                    [
                        {
                            text: 'Cancel',
                            onPress: () => console.log('Cancel Pressed'),
                            style: 'cancel',
                        },
                        { text: 'yes', onPress: async () => {await this.props.deleteComment(commentId);refreshPage();},
                    ],
                    { cancelable: false },
                );

Upvotes: 2

Sebastien Lorber
Sebastien Lorber

Reputation: 92190

Use react-native-alert-async

I've just published a package that does exactly this and permit to await for the choice of the user. It is compatible with Expo.

 import AlertAsync from "react-native-alert-async";


 const myAction = async () => {

   const choice = await AlertAsync(
     'Title',
     'Message',
     [
       {text: 'Yes', onPress: () => 'yes'},
       {text: 'No', onPress: () => Promise.resolve('no')},
     ],
     {
       cancelable: true,
       onDismiss: () => 'no',
     },
   );


   if (choice === 'yes') {
     doSomething();
   }
   else {
     doSomethingElse();
   }

 }

Original answer: I've made a PR to ReactNative for this feature: https://github.com/facebook/react-native/pull/20312

Upvotes: 7

nanda kishore reddy
nanda kishore reddy

Reputation: 604

React-native Alert doesn't stop the execution of code below it. By changing it to async function which resolves the promise on user action will work as ASYNC-Alert.

const AsyncAlert = async () => new Promise((resolve) => {
  Alert.alert(
    'info',
    'Message',
    [
      {
        text: 'ok',
        onPress: () => {
          resolve('YES');
        },
      },
    ],
    { cancelable: false },
  );
});

await AsyncAlert();

Upvotes: 38

niceman
niceman

Reputation: 361

Alert does not pause the code. In this case JS is not the only problem - the Camera component also keeps running in the background which is native and it will trigger the onBarCodeRead listener, regardless if the Alert is present or not.

You could try to stop the camera at the beginning on _onBarCodeRead with the stopPreview() method mentioned in the docs.

Also note that react-native-camera is currently in a migration process from Camera (RCTCamera) to RNCamera and in the new RNCamera I don't see a stopPreview() method. Anyhow, a simple flag would also do the job.

Upvotes: 2

Related Questions