Reputation: 2875
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
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
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
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
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
Reputation: 92190
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
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
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