[fix] AppState addEventListener returns subscription object

Close #2129
This commit is contained in:
Chris Zelenak
2021-09-16 14:22:23 -04:00
committed by Nicolas Gallagher
parent 4aba2bdcf4
commit 126642783b
4 changed files with 29 additions and 26 deletions
@@ -36,7 +36,7 @@ Returns the current state of the app.
### Static methods
{% call macro.prop('addEventListener', '(type: ?string, listener: (boolean) => void) => void') %}
{% call macro.prop('addEventListener', '(type: ?string, listener: (boolean) => void) => ?EmitterSubscription') %}
Add a listener to `AppState` changes. Listen to the `"change"` event type. The handler is called with the app state value.
{% endcall %}
+2 -2
View File
@@ -18,9 +18,9 @@ export default function AppStatePage() {
}));
};
AppState.addEventListener('change', handleChange);
const subscription = AppState.addEventListener('change', handleChange);
return () => {
AppState.removeEventListener('change', handleChange);
subscription.remove();
};
}, []);
@@ -5,22 +5,22 @@ import AppState from '..';
describe('apis/AppState', () => {
const handler = () => {};
afterEach(() => {
try {
AppState.removeEventListener('change', handler);
} catch (e) {}
});
describe('addEventListener', () => {
test('throws if the provided "eventType" is not supported', () => {
expect(() => AppState.addEventListener('foo', handler)).toThrow();
expect(() => AppState.addEventListener('change', handler)).not.toThrow();
expect(() => AppState.addEventListener('change', handler).remove()).not.toThrow();
});
});
describe('removeEventListener', () => {
test('throws if the handler is not registered', () => {
expect(() => AppState.removeEventListener('change', handler)).toThrow();
beforeEach(() => {
// removeEventListener logs a deprecation warning, ignore
jest.spyOn(console, 'error');
console.error.mockImplementation(() => {});
});
afterEach(() => {
console.error.mockRestore();
});
test('throws if the provided "eventType" is not supported', () => {
+17 -14
View File
@@ -9,8 +9,8 @@
*/
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import findIndex from 'array-find-index';
import invariant from 'fbjs/lib/invariant';
import EventEmitter from '../../vendor/react-native/emitter/_EventEmitter';
// Android 4.4 browser
const isPrefixed =
@@ -25,7 +25,7 @@ const AppStates = {
ACTIVE: 'active'
};
const listeners = [];
let changeEmitter = null;
export default class AppState {
static isAvailable = canUseDOM && document[VISIBILITY_STATE_PROPERTY];
@@ -53,9 +53,19 @@ export default class AppState {
type
);
if (type === 'change') {
const callback = () => handler(AppState.currentState);
listeners.push([handler, callback]);
document.addEventListener(VISIBILITY_CHANGE_EVENT, callback, false);
if (!changeEmitter) {
changeEmitter = new EventEmitter();
document.addEventListener(
VISIBILITY_CHANGE_EVENT,
() => {
if (changeEmitter) {
changeEmitter.emit('change', AppState.currentState);
}
},
false
);
}
return changeEmitter.addListener(type, handler);
}
}
}
@@ -67,15 +77,8 @@ export default class AppState {
'Trying to remove listener for unknown event: "%s"',
type
);
if (type === 'change') {
const listenerIndex = findIndex(listeners, (pair) => pair[0] === handler);
invariant(
listenerIndex !== -1,
'Trying to remove AppState listener for unregistered handler'
);
const callback = listeners[listenerIndex][1];
document.removeEventListener(VISIBILITY_CHANGE_EVENT, callback, false);
listeners.splice(listenerIndex, 1);
if (type === 'change' && changeEmitter) {
changeEmitter.removeListener(handler);
}
}
}