Skip to content

Commit

Permalink
RN: Refactor AccessibilityInfo Listeners
Browse files Browse the repository at this point in the history
Summary:
Refactors `AccessibilityInfo` so that it does not reimplement the event listener logic that is already implemented in `EventEmitter` (which backs the implementation of `RCTDeviceEventEmitter`).

This also means that calling `AccessibilityInfo.removeEventListener` will correctly display a deprecation error, due to `EventEmitter.removeListener` being deprecated. In a future release, both of these methods will be removed.

Changelog:
[General][Deprecated] - Deprecate `AccessibilityInfo.removeEventListener`.

Reviewed By: kacieb

Differential Revision: D27574340

fbshipit-source-id: 98c71d9c1470018df0f1526cc2f349aac842e786
  • Loading branch information
yungsters authored and facebook-github-bot committed Apr 6, 2021
1 parent 7ee2acc commit 003d63d
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 60 deletions.
49 changes: 25 additions & 24 deletions Libraries/Components/AccessibilityInfo/AccessibilityInfo.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* @flow strict-local
*/

import type EventEmitter from '../../vendor/emitter/EventEmitter';
import RCTDeviceEventEmitter from '../../EventEmitter/RCTDeviceEventEmitter';
import NativeAccessibilityInfo from './NativeAccessibilityInfo';
import type {EventSubscription} from 'react-native/Libraries/vendor/emitter/EventEmitter';
Expand All @@ -28,8 +29,6 @@ type AccessibilityEventDefinitions = {

type AccessibilityEventTypes = 'focus' | 'click';

const _subscriptions = new Map();

/**
* Sometimes it's useful to know whether or not the device has a screen reader
* that is currently active. The `AccessibilityInfo` API is designed for this
Expand Down Expand Up @@ -106,43 +105,45 @@ const AccessibilityInfo = {
eventName: K,
handler: (...$ElementType<AccessibilityEventDefinitions, K>) => void,
): EventSubscription {
let listener;

if (eventName === 'change' || eventName === 'screenReaderChanged') {
listener = RCTDeviceEventEmitter.addListener(
return RCTDeviceEventEmitter.addListener(
TOUCH_EXPLORATION_EVENT,
handler,
);
} else if (eventName === 'reduceMotionChanged') {
listener = RCTDeviceEventEmitter.addListener(
REDUCE_MOTION_EVENT,
handler,
);
}

// $FlowFixMe[escaped-generic]
_subscriptions.set(handler, listener);

if (eventName === 'reduceMotionChanged') {
return RCTDeviceEventEmitter.addListener(REDUCE_MOTION_EVENT, handler);
}
return {
remove: () => {
// $FlowIssue flow does not recognize handler properly
AccessibilityInfo.removeEventListener<K>(eventName, handler);
remove(): void {
// Do nothing.
},
};
},

/**
* @deprecated Use `remove` on the EventSubscription from `addEventListener`.
*/
removeEventListener: function<K: $Keys<AccessibilityEventDefinitions>>(
eventName: K,
handler: (...$ElementType<AccessibilityEventDefinitions, K>) => void,
): void {
// $FlowFixMe[escaped-generic]
const listener = _subscriptions.get(handler);
if (!listener) {
return;
// NOTE: This will report a deprecation notice via `console.error`.
if (eventName === 'change' || eventName === 'screenReaderChanged') {
// $FlowIgnore[incompatible-cast]
(RCTDeviceEventEmitter: EventEmitter<$FlowFixMe>).removeListener(
TOUCH_EXPLORATION_EVENT,
// $FlowFixMe[invalid-tuple-arity]
handler,
);
} else if (eventName === 'reduceMotionChanged') {
// $FlowIgnore[incompatible-cast]
(RCTDeviceEventEmitter: EventEmitter<$FlowFixMe>).removeListener(
REDUCE_MOTION_EVENT,
// $FlowFixMe[invalid-tuple-arity]
handler,
);
}
listener.remove();
// $FlowFixMe[escaped-generic]
_subscriptions.delete(handler);
},

/**
Expand Down
68 changes: 32 additions & 36 deletions Libraries/Components/AccessibilityInfo/AccessibilityInfo.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* @flow strict-local
*/

import type EventEmitter from '../../vendor/emitter/EventEmitter';
import RCTDeviceEventEmitter from '../../EventEmitter/RCTDeviceEventEmitter';
import NativeAccessibilityManager from './NativeAccessibilityManager';
import type {EventSubscription} from 'react-native/Libraries/vendor/emitter/EventEmitter';
Expand All @@ -16,15 +17,15 @@ import {sendAccessibilityEvent} from '../../Renderer/shims/ReactNative';
import legacySendAccessibilityEvent from './legacySendAccessibilityEvent';
import {type ElementRef} from 'react';

const CHANGE_EVENT_NAME = {
announcementFinished: 'announcementFinished',
boldTextChanged: 'boldTextChanged',
grayscaleChanged: 'grayscaleChanged',
invertColorsChanged: 'invertColorsChanged',
reduceMotionChanged: 'reduceMotionChanged',
reduceTransparencyChanged: 'reduceTransparencyChanged',
screenReaderChanged: 'screenReaderChanged',
};
const SupportedEvents: Set<string> = new Set([
'announcementFinished',
'boldTextChanged',
'grayscaleChanged',
'invertColorsChanged',
'reduceMotionChanged',
'reduceTransparencyChanged',
'screenReaderChanged',
]);

type AccessibilityEventDefinitions = {
boldTextChanged: [boolean],
Expand All @@ -46,8 +47,6 @@ type AccessibilityEventDefinitions = {
// 'click' event type is not implemented in iOS. It's declared here to avoid flow type errors
type AccessibilityEventTypes = 'focus' | 'click';

const _subscriptions = new Map();

/**
* Sometimes it's useful to know whether or not the device has a screen reader
* that is currently active. The `AccessibilityInfo` API is designed for this
Expand Down Expand Up @@ -217,24 +216,15 @@ const AccessibilityInfo = {
eventName: K,
handler: (...$ElementType<AccessibilityEventDefinitions, K>) => void,
): EventSubscription {
let subscription: EventSubscription;

if (eventName === 'change') {
subscription = RCTDeviceEventEmitter.addListener(
CHANGE_EVENT_NAME.screenReaderChanged,
handler,
);
} else if (CHANGE_EVENT_NAME[eventName]) {
subscription = RCTDeviceEventEmitter.addListener(eventName, handler);
return RCTDeviceEventEmitter.addListener('screenReaderChanged', handler);
}
if (SupportedEvents.has(eventName)) {
return RCTDeviceEventEmitter.addListener(eventName, handler);
}

// $FlowFixMe[escaped-generic]
_subscriptions.set(handler, subscription);

return {
remove: () => {
// $FlowIssue[incompatible-call] flow does not recognize handler properly
AccessibilityInfo.removeEventListener<K>(eventName, handler);
remove(): void {
// Do nothing.
},
};
},
Expand Down Expand Up @@ -271,22 +261,28 @@ const AccessibilityInfo = {
},

/**
* Remove an event handler.
*
* See https://reactnative.dev/docs/accessibilityinfo.html#removeeventlistener
* @deprecated Use `remove` on the EventSubscription from `addEventListener`.
*/
removeEventListener: function<K: $Keys<AccessibilityEventDefinitions>>(
eventName: K,
handler: (...$ElementType<AccessibilityEventDefinitions, K>) => void,
): void {
// $FlowFixMe[escaped-generic]
const listener = _subscriptions.get(handler);
if (!listener) {
return;
// NOTE: This will report a deprecation notice via `console.error`.
if (eventName === 'change') {
// $FlowIgnore[incompatible-cast]
(RCTDeviceEventEmitter: EventEmitter<$FlowFixMe>).removeListener(
'screenReaderChanged',
// $FlowFixMe[invalid-tuple-arity]
handler,
);
} else if (SupportedEvents.has(eventName)) {
// $FlowIgnore[incompatible-cast]
(RCTDeviceEventEmitter: EventEmitter<$FlowFixMe>).removeListener(
eventName,
// $FlowFixMe[invalid-tuple-arity]
handler,
);
}
listener.remove();
// $FlowFixMe[escaped-generic]
_subscriptions.delete(handler);
},
};

Expand Down

0 comments on commit 003d63d

Please sign in to comment.