Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
260 changes: 238 additions & 22 deletions docs/backhandler.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,63 +3,279 @@ id: backhandler
title: BackHandler
---

Detect hardware button presses for back navigation.
The Backhandler API detects hardware button presses for back navigation, lets you register event listeners for the system's back action, and lets you control how your application responds. It is Android-only.

Android: Detect hardware back button presses, and programmatically invoke the default back button functionality to exit the app if there are no listeners or if none of the listeners return true.
The event subscriptions are called in reverse order (i.e. the last registered subscription is called first).

tvOS: Detect presses of the menu button on the TV remote. (Still to be implemented: programmatically disable menu button handling functionality to exit the app if there are no listeners or if none of the listeners return true.)
- **If one subscription returns true,** then subscriptions registered earlier will not be called.
- **If no subscription returns true or none are registered,** it programmatically invokes the default back button functionality to exit the app.

iOS: Not applicable.
> **Warning for modal users:** If your app shows an opened `Modal`, `BackHandler` will not publish any events ([see `Modal` docs](modal#onrequestclose)).

The event subscriptions are called in reverse order (i.e. last registered subscription first), and if one subscription returns true then subscriptions registered earlier will not be called. Beware: If your app shows an opened `Modal`, BackHandler will not publish any events ([see `Modal` docs](modal#onrequestclose)).

Example:
## Pattern

```jsx
BackHandler.addEventListener('hardwareBackPress', function() {
// this.onMainScreen and this.goBack are just examples, you need to use your own implementation here
// Typically you would use the navigator here to go to the last state.
/**
* this.onMainScreen and this.goBack are just examples,
* you need to use your own implementation here.
*
* Typically you would use the navigator here to go to the last state.
*/

if (!this.onMainScreen()) {
this.goBack();
/**
* When true is returned the event will not be bubbled up
* & no other back action will execute
*/
return true;
}
/**
* Returning false will let the event to bubble up & let other event listeners
* or the system's default back action to be executed.
*/
return false;
});
```

Lifecycle example:
## Example

The following example implements a scenario where you confirm if the user wants to exit the app:

<div class="toggler">
<ul role="tablist" class="toggle-syntax">
<li id="functional" class="button-functional" aria-selected="false" role="tab" tabindex="0" aria-controls="functionaltab" onclick="displayTabs('syntax', 'functional')">
Function Component Example
</li>
<li id="classical" class="button-classical" aria-selected="false" role="tab" tabindex="0" aria-controls="classicaltab" onclick="displayTabs('syntax', 'classical')">
Class Component Example
</li>
</ul>
</div>

<block class="functional syntax" />

```SnackPlayer name=BackHandler&supportedPlatforms=android
import React, { useEffect } from "react";
import { Text, View, StyleSheet, BackHandler, Alert } from "react-native";

export default function App() {
useEffect(() => {
const backAction = () => {
Alert.alert("Hold on!", "Are you sure you want to go back?", [
{
text: "Cancel",
onPress: () => null,
style: "cancel"
},
{ text: "YES", onPress: () => BackHandler.exitApp() }
]);
return true;
};

const backHandler = BackHandler.addEventListener(
"hardwareBackPress",
backAction
);

return () => backHandler.remove();
}, []);

return (
<View style={styles.container}>
<Text style={styles.text}>Click Back button!</Text>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center"
},
text: {
fontSize: 18,
fontWeight: "bold"
}
});
```

<block class="classical syntax" />

```SnackPlayer name=BackHandler&supportedPlatforms=android
import React, { Component } from "react";
import { Text, View, StyleSheet, BackHandler, Alert } from "react-native";

export default class App extends Component {
backAction = () => {
Alert.alert("Hold on!", "Are you sure you want to go back?", [
{
text: "Cancel",
onPress: () => null,
style: "cancel"
},
{ text: "YES", onPress: () => BackHandler.exitApp() }
]);
return true;
};

```jsx
componentDidMount() {
this.backHandler = BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
this.backHandler = BackHandler.addEventListener(
"hardwareBackPress",
this.backAction
);
}

componentWillUnmount() {
this.backHandler.remove()
this.backHandler.remove();
}

handleBackPress = () => {
this.goBack(); // works best when the goBack is async
render() {
return (
<View style={styles.container}>
<Text style={styles.text}>Click Back button!</Text>
</View>
);
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center"
},
text: {
fontSize: 18,
fontWeight: "bold"
}
});
```

<block class="endBlock syntax" />

`BackHandler.addEventListener` creates an event listener & returns a `NativeEventSubscription` object which should be cleared using `NativeEventSubscription.remove` method.

Additionally `BackHandler.removeEventListener` can also be used to clear the event listener. Ensure the callback has the reference to the same function used in the `addEventListener` call as shown the following example ﹣

<div class="toggler">
<ul role="tablist" class="toggle-syntax">
<li id="functional" class="button-functional" aria-selected="false" role="tab" tabindex="0" aria-controls="functionaltab" onclick="displayTabs('syntax', 'functional')">
Function Component Example
</li>
<li id="classical" class="button-classical" aria-selected="false" role="tab" tabindex="0" aria-controls="classicaltab" onclick="displayTabs('syntax', 'classical')">
Class Component Example
</li>
</ul>
</div>

<block class="functional syntax" />

```SnackPlayer name=BackHandler&supportedPlatforms=android
import React, { useEffect } from "react";
import { Text, View, StyleSheet, BackHandler, Alert } from "react-native";

export default function App() {
const backAction = () => {
Alert.alert("Hold on!", "Are you sure you want to go back?", [
{
text: "Cancel",
onPress: () => null,
style: "cancel"
},
{ text: "YES", onPress: () => BackHandler.exitApp() }
]);
return true;
};

useEffect(() => {
BackHandler.addEventListener("hardwareBackPress", backAction);

return () =>
BackHandler.removeEventListener("hardwareBackPress", backAction);
}, []);

return (
<View style={styles.container}>
<Text style={styles.text}>Click Back button!</Text>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center"
},
text: {
fontSize: 18,
fontWeight: "bold"
}
});
```

Lifecycle alternative:
<block class="classical syntax" />

```SnackPlayer name=BackHandler&supportedPlatforms=android
import React, { Component } from "react";
import { Text, View, StyleSheet, BackHandler, Alert } from "react-native";

export default class App extends Component {
backAction = () => {
Alert.alert("Hold on!", "Are you sure you want to go back?", [
{
text: "Cancel",
onPress: () => null,
style: "cancel"
},
{ text: "YES", onPress: () => BackHandler.exitApp() }
]);
return true;
};

```jsx
componentDidMount() {
this.backHandler = BackHandler.addEventListener('hardwareBackPress', () => {
this.goBack(); // works best when the goBack is async
return true;
});
BackHandler.addEventListener("hardwareBackPress", this.backAction);
}

componentWillUnmount() {
this.backHandler.remove();
BackHandler.removeEventListener("hardwareBackPress", this.backAction);
}

render() {
return (
<View style={styles.container}>
<Text style={styles.text}>Click Back button!</Text>
</View>
);
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center"
},
text: {
fontSize: 18,
fontWeight: "bold"
}
});
```

<block class="endBlock syntax" />

## Usage with React Navigation

If you are using React Navigation to navigate across different screens, you can follow their guide on [Custom Android back button behaviour](https://reactnavigation.org/docs/custom-android-back-button-handling/)

## Backhandler hook

[React Native Hooks](https://github.com/react-native-community/hooks#usebackhandler) has a nice `useBackHandler` hook which will simplify the process of setting up event listeners.

---

# Reference
Expand Down