react-native-macos/Libraries/EventEmitter/NativeEventEmitter.js

123 строки
3.8 KiB
JavaScript
Исходник Обычный вид История

Added native event emitter Summary: This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/ I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already). Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`. JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing: const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... }); You'd now write: const Keyboard = require('Keyboard'); Keyboard.addListener('keyboardWillShow', (event) => { ... }); Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of: this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...); Write: this.addListenerOn(Keyboard, 'keyboardWillShow', ...); This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module. As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff. For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface. Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem). Reviewed By: javache Differential Revision: D3269966 fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
Added native event emitter Summary: This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/ I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already). Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`. JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing: const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... }); You'd now write: const Keyboard = require('Keyboard'); Keyboard.addListener('keyboardWillShow', (event) => { ... }); Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of: this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...); Write: this.addListenerOn(Keyboard, 'keyboardWillShow', ...); This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module. As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff. For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface. Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem). Reviewed By: javache Differential Revision: D3269966 fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
Added native event emitter Summary: This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/ I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already). Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`. JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing: const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... }); You'd now write: const Keyboard = require('Keyboard'); Keyboard.addListener('keyboardWillShow', (event) => { ... }); Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of: this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...); Write: this.addListenerOn(Keyboard, 'keyboardWillShow', ...); This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module. As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff. For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface. Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem). Reviewed By: javache Differential Revision: D3269966 fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
*
* @flow strict-local
* @format
Added native event emitter Summary: This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/ I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already). Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`. JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing: const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... }); You'd now write: const Keyboard = require('Keyboard'); Keyboard.addListener('keyboardWillShow', (event) => { ... }); Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of: this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...); Write: this.addListenerOn(Keyboard, 'keyboardWillShow', ...); This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module. As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff. For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface. Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem). Reviewed By: javache Differential Revision: D3269966 fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
*/
Added native event emitter Summary: This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/ I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already). Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`. JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing: const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... }); You'd now write: const Keyboard = require('Keyboard'); Keyboard.addListener('keyboardWillShow', (event) => { ... }); Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of: this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...); Write: this.addListenerOn(Keyboard, 'keyboardWillShow', ...); This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module. As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff. For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface. Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem). Reviewed By: javache Differential Revision: D3269966 fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
'use strict';
import type {
EventSubscription,
IEventEmitter,
} from '../vendor/emitter/EventEmitter';
import Platform from '../Utilities/Platform';
import RCTDeviceEventEmitter from './RCTDeviceEventEmitter';
import invariant from 'invariant';
Added native event emitter Summary: This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/ I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already). Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`. JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing: const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... }); You'd now write: const Keyboard = require('Keyboard'); Keyboard.addListener('keyboardWillShow', (event) => { ... }); Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of: this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...); Write: this.addListenerOn(Keyboard, 'keyboardWillShow', ...); This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module. As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff. For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface. Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem). Reviewed By: javache Differential Revision: D3269966 fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
Use native module passed to NativeEventEmitter on Android Summary: ## Context In native modules implementing an event emitter, we can wait for JS to subscribe to an event before making the subscription to the right native API in the native module. This is only supported on iOS at the moment and we want to support it on Android too, so we can manage resources more efficiently and avoid custom code to do this on Android, like this: https://www.internalfb.com/intern/diffusion/FBS/browse/master/xplat/js/RKJSModules/public/Dating/Profile/ProfileView/ProfileGemstoneProfileView.js?commit=165ad219e6bf&lines=302-304 The way this works now is by creating instances of `NativeEventEmitter`, where we pass a reference to the native module that needs to be notified when there are new subscriptions. We have explicit code to ignore this native modules in all platforms except for iOS: https://www.internalfb.com/intern/diffusion/FBS/browsefile/master/xplat/js/react-native-github/Libraries/EventEmitter/NativeEventEmitter.js?commit=5a1e671453465e844dd851c458cb2467a2db5d03&lines=44-52 ## Changes This removes the check for iOS from `NativeEventEmitter` so we also try to use the native module to notify subscriptions on Android. We have migrated all existing code passing a native module to `NativeEventEmtiter` to only pass it on iOS, so we don't change this behavior in existing code. Any other existing code using this API is most likely fine too. It didn't work before so the expectation is that the native module wouldn't be implemented on Android anyway. Changelog: [Android][Changed] - Modified `NativeEventEmitter` to also use the passed native module to report subscriptions on Android Reviewed By: yungsters Differential Revision: D27500994 fbshipit-source-id: ef82da04020fb08cd0ea4f1cfffd1da6453ab0b9
2021-04-12 16:23:53 +03:00
interface NativeModule {
addListener(eventType: string): void;
removeListeners(count: number): void;
}
export type {EventSubscription};
Added native event emitter Summary: This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/ I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already). Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`. JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing: const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... }); You'd now write: const Keyboard = require('Keyboard'); Keyboard.addListener('keyboardWillShow', (event) => { ... }); Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of: this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...); Write: this.addListenerOn(Keyboard, 'keyboardWillShow', ...); This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module. As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff. For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface. Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem). Reviewed By: javache Differential Revision: D3269966 fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
/**
* `NativeEventEmitter` is intended for use by Native Modules to emit events to
* JavaScript listeners. If a `NativeModule` is supplied to the constructor, it
* will be notified (via `addListener` and `removeListeners`) when the listener
* count changes to manage "native memory".
*
* Currently, all native events are fired via a global `RCTDeviceEventEmitter`.
* This means event names must be globally unique, and it means that call sites
* can theoretically listen to `RCTDeviceEventEmitter` (although discouraged).
Added native event emitter Summary: This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/ I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already). Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`. JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing: const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... }); You'd now write: const Keyboard = require('Keyboard'); Keyboard.addListener('keyboardWillShow', (event) => { ... }); Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of: this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...); Write: this.addListenerOn(Keyboard, 'keyboardWillShow', ...); This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module. As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff. For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface. Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem). Reviewed By: javache Differential Revision: D3269966 fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
*/
export default class NativeEventEmitter<TEventToArgsMap: {...}>
implements IEventEmitter<TEventToArgsMap>
{
_nativeModule: ?NativeModule;
Added native event emitter Summary: This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/ I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already). Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`. JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing: const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... }); You'd now write: const Keyboard = require('Keyboard'); Keyboard.addListener('keyboardWillShow', (event) => { ... }); Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of: this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...); Write: this.addListenerOn(Keyboard, 'keyboardWillShow', ...); This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module. As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff. For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface. Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem). Reviewed By: javache Differential Revision: D3269966 fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
constructor(nativeModule: ?NativeModule) {
Added native event emitter Summary: This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/ I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already). Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`. JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing: const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... }); You'd now write: const Keyboard = require('Keyboard'); Keyboard.addListener('keyboardWillShow', (event) => { ... }); Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of: this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...); Write: this.addListenerOn(Keyboard, 'keyboardWillShow', ...); This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module. As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff. For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface. Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem). Reviewed By: javache Differential Revision: D3269966 fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
if (Platform.OS === 'ios') {
invariant(
nativeModule != null,
'`new NativeEventEmitter()` requires a non-null argument.',
);
Added native event emitter Summary: This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/ I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already). Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`. JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing: const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... }); You'd now write: const Keyboard = require('Keyboard'); Keyboard.addListener('keyboardWillShow', (event) => { ... }); Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of: this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...); Write: this.addListenerOn(Keyboard, 'keyboardWillShow', ...); This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module. As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff. For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface. Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem). Reviewed By: javache Differential Revision: D3269966 fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
}
const hasAddListener =
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
!!nativeModule && typeof nativeModule.addListener === 'function';
const hasRemoveListeners =
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
!!nativeModule && typeof nativeModule.removeListeners === 'function';
if (nativeModule && hasAddListener && hasRemoveListeners) {
this._nativeModule = nativeModule;
} else if (nativeModule != null) {
if (!hasAddListener) {
console.warn(
'`new NativeEventEmitter()` was called with a non-null argument without the required `addListener` method.',
);
}
if (!hasRemoveListeners) {
console.warn(
'`new NativeEventEmitter()` was called with a non-null argument without the required `removeListeners` method.',
);
}
}
Added native event emitter Summary: This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/ I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already). Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`. JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing: const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... }); You'd now write: const Keyboard = require('Keyboard'); Keyboard.addListener('keyboardWillShow', (event) => { ... }); Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of: this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...); Write: this.addListenerOn(Keyboard, 'keyboardWillShow', ...); This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module. As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff. For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface. Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem). Reviewed By: javache Differential Revision: D3269966 fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
}
addListener<TEvent: $Keys<TEventToArgsMap>>(
eventType: TEvent,
listener: (...args: $ElementType<TEventToArgsMap, TEvent>) => mixed,
context?: mixed,
): EventSubscription {
this._nativeModule?.addListener(eventType);
let subscription: ?EventSubscription = RCTDeviceEventEmitter.addListener(
eventType,
listener,
context,
);
return {
remove: () => {
if (subscription != null) {
this._nativeModule?.removeListeners(1);
// $FlowFixMe[incompatible-use]
subscription.remove();
subscription = null;
}
},
};
Added native event emitter Summary: This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/ I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already). Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`. JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing: const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... }); You'd now write: const Keyboard = require('Keyboard'); Keyboard.addListener('keyboardWillShow', (event) => { ... }); Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of: this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...); Write: this.addListenerOn(Keyboard, 'keyboardWillShow', ...); This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module. As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff. For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface. Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem). Reviewed By: javache Differential Revision: D3269966 fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
}
emit<TEvent: $Keys<TEventToArgsMap>>(
eventType: TEvent,
...args: $ElementType<TEventToArgsMap, TEvent>
): void {
// Generally, `RCTDeviceEventEmitter` is directly invoked. But this is
// included for completeness.
RCTDeviceEventEmitter.emit(eventType, ...args);
Added native event emitter Summary: This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/ I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already). Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`. JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing: const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... }); You'd now write: const Keyboard = require('Keyboard'); Keyboard.addListener('keyboardWillShow', (event) => { ... }); Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of: this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...); Write: this.addListenerOn(Keyboard, 'keyboardWillShow', ...); This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module. As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff. For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface. Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem). Reviewed By: javache Differential Revision: D3269966 fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
}
removeAllListeners<TEvent: $Keys<TEventToArgsMap>>(
eventType?: ?TEvent,
): void {
invariant(
eventType != null,
'`NativeEventEmitter.removeAllListener()` requires a non-null argument.',
);
this._nativeModule?.removeListeners(this.listenerCount(eventType));
RCTDeviceEventEmitter.removeAllListeners(eventType);
}
listenerCount<TEvent: $Keys<TEventToArgsMap>>(eventType: TEvent): number {
return RCTDeviceEventEmitter.listenerCount(eventType);
Added native event emitter Summary: This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/ I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already). Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`. JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing: const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... }); You'd now write: const Keyboard = require('Keyboard'); Keyboard.addListener('keyboardWillShow', (event) => { ... }); Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of: this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...); Write: this.addListenerOn(Keyboard, 'keyboardWillShow', ...); This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module. As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff. For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface. Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem). Reviewed By: javache Differential Revision: D3269966 fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
}
}