-
Notifications
You must be signed in to change notification settings - Fork 24.5k
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
Broken BackAndroid handling #11968
Comments
Do you have any code samples that show how the error can be reproduced? |
I have this same issue in RN 0.43.4. I can confirm that:
|
I believe I have tracked down the underlying issue. At the start of my code I
If I remove all code that leads to a Workaround 1Remove anything that depends on a Workaround 2After requiring something that uses a {
// https://github.com/facebook/react-native/issues/11968
// RN currently has a custom `Set` polyfill that is broken when you use an actual Symbol polyfill
let toIterator = require('react-native/Libraries/vendor/core/toIterator');
if ( toIterator.ITERATOR_SYMBOL !== Symbol.iterator && Set.prototype[toIterator.ITERATOR_SYMBOL] ) {
Set.prototype[Symbol.iterator] = Set.prototype[toIterator.ITERATOR_SYMBOL];
}
} Short-term fixBackAndroid (now BackHandler.android) doesn't actually have any reason to depend on fancy iterator behaviours. The line immediately after the ES6 A simple fix would be to replace this: var backPressSubscriptions = new Set(_backPressSubscriptions);
var invokeDefault = true;
var subscriptions = [...backPressSubscriptions].reverse(); with this: var invokeDefault = true;
var subscriptions = [];
_backPressSubscriptions.forEach(function(subscription) {
subscriptions.unshift(subscription);
}); (If you think Long-term fixLong term this is going to be a general problem with this pattern of React Native hardcoding its own embedded polyfill and likely will result in other parts of React Native breaking – the short-term fix after all doesn't actually fix React Native should either abandon low-level ES6 polyfills with conflict potential like |
haha I can't believe I just spent an entire day trying to figure out why the back button wasn't working. It's the issue @dantman described. I was importing the Symbol polyfill from core-js. Back button was working on chrome debug mode, not in the regular app. |
@dantman |
Hi there! This issue is being closed because it has been inactive for a while. Maybe the issue has been fixed in a recent release, or perhaps it is not affecting a lot of people. Either way, we're automatically closing issues after a period of inactivity. Please do not take it personally! If you think this issue should definitely remain open, please let us know. The following information is helpful when it comes to determining if the issue should be re-opened:
If you would like to work on a patch to fix the issue, contributions are very welcome! Read through the contribution guide, and feel free to hop into #react-native if you need help planning your contribution. |
This usage currently causes BackHandler to silently become non-functional when a Symbol polyfill is used. Why? - The [...obj] operator implicitly calls the object's iterator to fill the array - react-native's internal Set polyfill has a load order issue that breaks it whenever a project uses a Symbol polyfill - This means that when BackHandler tries to [...set] a Set of subscriptions the result is always an empty array instead of an array of subscriptions Additionally it's worth noting that the current code is also wastefully inefficient as in order to reverse iterate over subscriptions it: - Clones the Set (which fills it by implicitly running the set's iterator) - Uses [...set] to convert the cloned set into an array (which also implicitly runs the iterator on the clone) - Finally reverses the order of the array This code fixes this problem by replacing the use of multiple Set instance iterators with a simple .forEach loop that unshifts directly into the final array. Fixes facebook#11968
I've added a PR |
This usage currently causes BackHandler to silently become non-functional when a Symbol polyfill is used. Why? - The [...obj] operator implicitly calls the object's iterator to fill the array - react-native's internal Set polyfill has a load order issue that breaks it whenever a project uses a Symbol polyfill - This means that when BackHandler tries to [...set] a Set of subscriptions the result is always an empty array instead of an array of subscriptions Additionally it's worth noting that the current code is also wastefully inefficient as in order to reverse iterate over subscriptions it: - Clones the Set (which fills it by implicitly running the set's iterator) - Uses [...set] to convert the cloned set into an array (which also implicitly runs the iterator on the clone) - Finally reverses the order of the array This code fixes this problem by replacing the use of multiple Set instance iterators with a simple .forEach loop that unshifts directly into the final array. Fixes facebook#11968
This usage currently causes BackHandler to silently become non-functional when a Symbol polyfill is used. Why? - The [...obj] operator implicitly calls the object's iterator to fill the array - react-native's internal Set polyfill has a load order issue that breaks it whenever a project uses a Symbol polyfill - This means that when BackHandler tries to [...set] a Set of subscriptions the result is always an empty array instead of an array of subscriptions Additionally it's worth noting that the current code is also wastefully inefficient as in order to reverse iterate over subscriptions it: - Clones the Set (which fills it by implicitly running the set's iterator) - Uses [...set] to convert the cloned set into an array (which also implicitly runs the iterator on the clone) - Finally reverses the order of the array This code fixes this problem by replacing the use of multiple Set instance iterators with a simple .forEach loop that unshifts directly into the final array. Fixes facebook#11968
This usage currently causes BackHandler to silently become non-functional when a Symbol polyfill is used. Why? - The [...obj] operator implicitly calls the object's iterator to fill the array - react-native's internal Set polyfill has a load order issue that breaks it whenever a project uses a Symbol polyfill - This means that when BackHandler tries to [...set] a Set of subscriptions the result is always an empty array instead of an array of subscriptions Additionally it's worth noting that the current code is also wastefully inefficient as in order to reverse iterate over subscriptions it: - Clones the Set (which fills it by implicitly running the set's iterator) - Uses [...set] to convert the cloned set into an array (which also implicitly runs the iterator on the clone) - Finally reverses the order of the array This code fixes this problem by replacing the use of multiple Set instance iterators with a simple .forEach loop that unshifts directly into the final array. Fixes facebook#11968
This usage currently causes BackHandler to silently become non-functional when a Symbol polyfill is used. Why? - The [...obj] operator implicitly calls the object's iterator to fill the array - react-native's internal Set polyfill has a load order issue that breaks it whenever a project uses a Symbol polyfill - This means that when BackHandler tries to [...set] a Set of subscriptions the result is always an empty array instead of an array of subscriptions Additionally it's worth noting that the current code is also wastefully inefficient as in order to reverse iterate over subscriptions it: - Clones the Set (which fills it by implicitly running the set's iterator) - Uses [...set] to convert the cloned set into an array (which also implicitly runs the iterator on the clone) - Finally reverses the order of the array This code fixes this problem by replacing the use of multiple Set instance iterators with use of `set.values()` which does not require the set's implicit iterator. Fixes facebook#11968
Summary: This usage of `...` currently causes BackHandler to silently become non-functional when a `Symbol` polyfill is used. Why? - The `[...obj]` operator implicitly calls the object's iterator to fill the array - react-native's internal `Set` polyfill has a load order issue that breaks it whenever a project uses a `Symbol` polyfill - This means that when `BackHandler` tries to `[...set]` a `Set` of subscriptions the result is always an empty array instead of an array of subscriptions Additionally it's worth noting that the current code is also wastefully inefficient as in order to reverse iterate over subscriptions it: - Clones the `Set` (which fills it by implicitly running the set's iterator) - Uses `[...set]` to convert the cloned set into an array (which also implicitly runs the iterator on the clone) - Finally reverses the order of the array ---- This code fixes this problem by replacing the use of multiple Set instance iterators with a simple `.forEach` loop that unshifts directly into the final array. I've tested this by opening the repo's RNTester app on Android and tvOS ensuring that the back handler works before changes, the application crashes when I introduce an error (to verify my code changes are being applied to the app), the back handler works and after changes. Fixes #11968 Closes #15182 Differential Revision: D6114696 Pulled By: hramos fbshipit-source-id: 2eae127558124a394bb3572a6381a5985ebf9d64
For anyone still having this issue, since the commit 165b38a didn't actually fix it, you can use the fix by dantman by creating a new file where BackHandler doesn't use [...] or Set.values:
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
"use strict";
var DeviceEventManager = require("NativeModules").DeviceEventManager;
var RCTDeviceEventEmitter = require("RCTDeviceEventEmitter");
var DEVICE_BACK_EVENT = "hardwareBackPress";
type BackPressEventName = $Enum<{
backPress: string,
}>;
var _backPressSubscriptions = new Set();
RCTDeviceEventEmitter.removeAllListeners(DEVICE_BACK_EVENT);
RCTDeviceEventEmitter.addListener(DEVICE_BACK_EVENT, function() {
var invokeDefault = true;
var subscriptions = [];
_backPressSubscriptions.forEach(sub => subscriptions.unshift(sub));
for (var i = 0; i < subscriptions.length; ++i) {
if (subscriptions[i]()) {
invokeDefault = false;
break;
}
}
if (invokeDefault) {
BackHandler.exitApp();
}
});
/**
* Detect hardware button presses for back navigation.
*
* 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.
*
* 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.)
*
* iOS: Not applicable.
*
* 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.
*
* Example:
*
* ```javascript
* 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.
*
* if (!this.onMainScreen()) {
* this.goBack();
* return true;
* }
* return false;
* });
* ```
*/
var BackHandler = {
exitApp: function() {
DeviceEventManager.invokeDefaultBackPressHandler();
},
/**
* Adds an event handler. Supported events:
*
* - `hardwareBackPress`: Fires when the Android hardware back button is pressed or when the
* tvOS menu button is pressed.
*/
addEventListener: function(
eventName: BackPressEventName,
handler: Function
): {remove: () => void} {
_backPressSubscriptions.add(handler);
return {
remove: () => BackHandler.removeEventListener(eventName, handler),
};
},
/**
* Removes the event handler.
*/
removeEventListener: function(eventName: BackPressEventName, handler: Function): void {
_backPressSubscriptions.delete(handler);
},
};
module.exports = BackHandler; And then |
Thanks, I've used this custom Nevertheless I needed to import it with a new name : |
@cosmith Tried this. It still doesn't work for me. In my App.js added event listener for hardwareBackPress like this
|
@rohitgoyal maybe try importing it with another name like AuroreM |
We use GitHub Issues for bugs.
Description
Handling back button with BackAndroid does not work - while adding handlers works fine they are never called. The reason behind this -
Array.from(new Set([1, 2, 3]))
returns empty array. Sets are used by theBackAndroid
module.Reproduction
Additional Information
The text was updated successfully, but these errors were encountered: