1. ホーム
  2. React-Native

React-NativeにおけるイベントリスナーDeviceEventEmitter

2022-02-14 10:08:12
<パス

基本的な構文

react-nativeでのイベントリスニングは、DeviceEventEmitterというプラグインを使って行います。ここでは、基本的な構文について、以下のように説明します。

1. リスナーの設定

リスナーを受け取りたい場所に、one.jsのページの場合、リスナーを追加する

import { DeviceEventEmitter } from 'react-native';
...
componentDidMount() {
    //Receive a listener
    this.listener = DeviceEventEmitter.addListener('notification name', (message) => {
    //what you want to do when you receive the listener
    console.log(message); //Listen
    })
}
componentWillUnmount() {
    //Remove the listener
    if (this.listener) {
      this.listener.remove();
    }
  }
...


上記のコードから、リスナーはDeviceEventEmitterを介して設定され、React-Nativeのライフサイクルに沿って、コンポーネントがロードされたときにリスニングし、コンポーネントがアンロードされたときにリスナーを削除していることがわかります

2. リスナーのトリガー

one.jsのページでリスナーをトリガーする必要がある場合、別のページでペナルティを課すことができます。トリガーするページを2.jsと仮定すると、コードは次のようになります。

import { DeviceEventEmitter } from 'react-native';
...
startEmit() {
    //prepare the value and send the listener
    const message = 'listen';
    DeviceEventEmitter.emit('notification name', message);
}
...


インスタンス操作

ここでは、グローバルリスニングイベントを検証するために2つのページを使用して、ここでは"react-navigation"("1.5.12")、ジャンプインタフェース、まず我々は最初のインターフェイスを実装するために行く、このインタフェースではリスナーを設定するには、コードを次に示すように使用されています。

import React, { Component } from 'react';
import { Button, Text, View, DeviceEventEmitter } from 'react-native';

class One extends Component {
  constructor(props) {
    super(props);
    this.state = { result: 'I am the default' }
  }
  componentDidMount() {
    // Receive a listener
    this.listener = DeviceEventEmitter.addListener('changeResult', (message) => {
      // What you want to do when you receive a listener // Listening
      this.setState({ result: message });
    });
  }
  componentWillUnmount() {
    // Remove the listener
    if (this.listener) { this.listener.remove(); }
  }

  _onChange = () => {
    const { navigation } = this.props;
    navigation.navigate('Two');
  };

  render() {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <Button onPress={this._onChange} title="Jump to second page" />
        <Text style={{ fontSize: 20, marginTop: 30 }}>{this.state.result}</Text>
      </View>
    );
  }
}
export default One;


その後、2番目のインターフェースを完成させます。ここでは、1番目のインターフェースをリッスンしているイベントのトリガーを次のコードで実行します。

import React, { Component } from 'react';
import { Button, View, DeviceEventEmitter } from 'react-native';
class Two extends Component {
  startEmit = () => {
    // Prepare the value, send the listener
    const message = 'The listener was sent through to allow the value of the one page to be changed';
    DeviceEventEmitter.exit('changeResult', message);
  };

  render() {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <Button onPress={this.startEmit} title="Triggers the first page's listener event" />
      </View>
    );
  }
}
export default Two;



では、どのように見えるか見てみましょう。

<センター [エミット]

ソースコードの説明

基本的な使い方ができたので、one.jsのページでクリック(command+左クリック)してソースコードの情報を見ることができるかどうか調べてみましょう。 DeviceEventEmitter プラグインを使用すると、次のようなインターフェイス・コードが表示されます。

<センター DeviceEventEmitter

プラグインでは、次のようなコードがあります。 get DeviceEventEmitter() { return require('RCTDeviceEventEmitter'); }, これはつまり DeviceEventEmitter 実装の基本はRCTDeviceEventEmitterなので、さらに下を見ると、Libarayフォルダに以下のようなインタフェースがあります。

<センター [エミット] 次に、RCTDeviceEventEmitter.js ファイルを探し、そこからコピーした以下のコードを見つけてください。
/**
 * Deprecated - subclass NativeEventEmitter to create granular event modules instead of
 * adding all event listeners directly to RCTDeviceEventEmitter.
 */
class RCTDeviceEventEmitter extends EventEmitter {
  sharedSubscriber: EventSubscriptionVendor;
  constructor() {
    const sharedSubscriber = new EventSubscriptionVendor();
    super(sharedSubscriber);
    this.sharedSubscriber = sharedSubscriber;
  }
  addListener(eventType: string, listener: Function, context: ?Object): EmitterSubscription {
    if (__DEV__) {
      checkNativeEventModule(eventType);
    }
    return super.addListener(eventType, listener, context);
  }
  removeAllListeners(eventType: ?string) {
    if (__DEV__) {
      checkNativeEventModule(eventType);
    }
    super.removeAllListeners(eventType);
  }
  removeSubscription(subscription: EmitterSubscription) {
    if (subscription.emitter ! == this) {
      subscription.emitter.removeSubscription(subscription);
    } else {
      super.removeSubscription(subscription);
    }
  }
}

module.exports = new RCTDeviceEventEmitter();



コードから、実装されているメソッドはaddListenerであることがわかりますが、それ以外に使用しているメソッドがないことがわかります。

const EventEmitter = require('EventEmitter');
const EventSubscriptionVendor = require('EventSubscriptionVendor');


そこで、次のようなインターフェイスを見ることができる。

<センター

必要なファイルを見るには、EventEmitter.jsファイルを開くと、それを実装したコードが以下のように表示されます。

/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * This source code is licensed under the MIT license found in the
 * This source code is licensed under the MIT license found in the
 This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree.
 *
 * @providesModule EventEmitter
 * @noflow
 * @typecheck
 */
'use strict';

const EmitterSubscription = require('EmitterSubscription');
const EventSubscriptionVendor = require('EventSubscriptionVendor');

const emptyFunction = require('fbjs/lib/emptyFunction');
const invariant = require('fbjs/lib/invariant');

/**
 * @class EventEmitter
 * @description
 * An EventEmitter is responsible for managing a set of listeners and publishing
 * In addition to the
 In addition to the * data for the given event it also sends a event control object which allows
 In addition to the * data for the given event it also sends an event control object which allows * the listeners/handlers to prevent the default behavior of the given event.
 *The emitter is designed to prevent the default behavior of the given event.
 * The emitter is designed to be generic enough to support all the different
 * The emitter is designed to be generic enough to support all the different contexts in which one might want to emit events.
 It is a simple multicast * mechanism on top of which extra functionality can be composed.
 For example, a * more advanced emitter may use an EventHolder and EventFactory.
 */For example, a * more advanced emitter may use an EventHolder and EventFactory.
class EventEmitter {

  _subscriber: EventSubscriptionVendor;
  _currentSubscription: ?EmitterSubscription;

  /* ***
   * @constructor
   *
   * @param {EventSubscriptionVendor} subscriber - Optional subscriber instance
   * If omitted, a new subscriber will be created for the emitter.
   */
  constructor(subscriber: ?EventSubscriptionVendor) {
    this._subscriber = subscriber || new EventSubscriptionVendor();
  }

  /**
   Adds a listener to be invoked when events of the specified type are * emitted.
   * An optional calling context may be provided.
   * An optional calling context may be provided.
   The data arguments * emitted will be passed to the listener function.
   * TODO: Annotate the listener arg's type.
   This is tricky because listeners * can be invoked with varargs.
   This is tricky because listeners * can be invoked with varargs.
   * @param {string} eventType - Name of the event to listen to.
   * @param {function} listener - Function to invoke when the specified event is
   * emitted
   * @param {*} context - Optional context object to use when invoking the
   * listener
   */
  addListener(
    eventType: string, listener: Function, context: ?Object): EmitterSubscription {

    return (this._subscriber.addSubscription(
      eventType,
      new EmitterSubscription(this, this._subscriber, listener, context)
    ) : any);
  }

  /**
   * Similar to addListener, except that the listener is removed after it is
   This is the same as addListener, except that the listener is removed after it is * invoked once.
   * @param {string}
   * @param {string} eventType - Name of the event to listen to
   * @param {function} listener - Function to invoke only once when the
   function to invoke only once when the * specified event is emitted
   * @param {*} context - Optional context object to use when invoking the
   * listener
   */
  once(eventType: string, listener: Function, context: ?Object): EmitterSubscription {
    return this.addListener(eventType, (.. .args) => {
      this.removeCurrentListener();
      listener.apply(context, args);
    });
  }

  /**
   * Removes all of the registered listeners, including those registered as
   * listener maps.
   * @param {?string}
   * @param {?string} eventType - Optional name of the event whose registered
   *
   * Provides an API that can be called during an eventing cycle to remove the
   * This allows a developer to provide an event
   * object that can remove the listener (or listener map) during the
   * invocation.
   If it is called when not inside the * invocation.
   * If it is called when not inside of an emitting cycle it will throw.
   * If it is called when not inside of an emitting cycle it will throw.
   * @throws {Error} When called not during an eventing cycle
   * @example
   * @example
   * var subscription = emitter.addListenerMap({
   * someEvent: function(data, event) {
   * console.log(data);
   * emitter.removeCurrentListener();
   * }
   * });
   *
   * emitter.exit('someEvent', 'abc'); // logs 'abc'
   * emitter.emit('someEvent', 'def'); // does not log anything
   */
  removeCurrentListener() {
    invariant(
      ! !this._currentSubscription,
      'Not in an emitting cycle; there is no current subscription'
    );
    this.removeSubscription(this._currentSubscription);
  }

  /* * Removes a specific subscription.
   * Called by the `remove()` method of the
   Called by the `remove()` method of the * subscription itself to ensure any necessary cleanup is performed.
   */Called by the `remove()` method of the * subscription itself to ensure any necessary cleanup is performed.
  removeSubscription(subscription: EmitterSubscription) {
    invariant(
      subscription.emitter === this,
      'Subscription does not belong to this emitter.'
    );
    this._subscriber.removeSubscription(subscription);
  }

  /**
   * Returns an array of listeners that are currently registered for the given
   * event.
   *
   * @param {string} eventType - Name of the event to query
   * @returns {array}
   */
  listeners(eventType: string): [EmitterSubscription] {
    const subscriptions: ? [EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any);
    return subscriptions
      ? subscriptions.filter(emptyFunction.thatReturnsTrue).map(
          function(subscription) {
            return subscription.listener;
          })
      : [];
  }

  /* Emits an event of the given type with the given data.
   * Emits an event of the given type with the given data.
   All handlers of that * particular type will be notified.
   All handlers of that * particular type will be notified.
   * @param {string} eventType - Name of the event to emit
   * @param {... *} Arbitrary arguments to be passed to each registered listener
   * @example
   * @example
   * emitter.addListener('someEvent', function(message) {
   * console.log(message);
   * });
   *
   * emitter.emit('someEvent', 'abc'); // logs 'abc'
   */
  emit(eventType: string) {
    const subscriptions: ? [EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any);
    if (subscriptions) {
      for (let i = 0, l = subscriptions.length; i < l; i++) {
        const subscription = subscriptions[i];

        // The subscription may have been removed during this event loop.
        if (subscription) {
          this._currentSubscription = subscription;
          subscription.listener.apply(
            subscription.context,
            Array.prototype.slice.call(arguments, 1)
          );
        }
      }
      this._currentSubscription = null;
    }
  }

  /**
   * Removes the given listener for event of specific type.
   * @param {string} eventType - Name of the event to emit.
   * @param {string} eventType - Name of the event to emit
   * @param {function} listener - Function to invoke when the specified event is
   * emitted
   * @example
   * @example
   * emitter.removeListener('someEvent', function(message) {
   * console.log(message);
   * }); // removes the listener if already registered
   *
   */
  removeListener(eventType: String, listener) {
    const subscriptions: ? [EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any);
    if (subscriptions) {
      for (le

上記のコードの中で、one.jsやtwo.jsで使っているメソッド、例えば、string型のイベントタイプ、リスニングに使うメソッド、コンテキストを渡すことでサブスクリプションを追加するaddListenerは、以下のように見つけることができました。

  addListener(
    eventType: string, listener: Function, context: ?Object): EmitterSubscription {

    return (this._subscriber.addSubscription(
      eventType,
      new EmitterSubscription(this, this._subscriber, listener, context)
    ) : any);
  }


また、次のコードに示すように、イベントタイプ別にサブスクライブされたすべてのイベントを反復して、指定された eventType に対するサブスクリプションの詳細を取得し、そのパラメータを apply メソッド経由でコンテキストに渡す exit メソッドもあります。

  emit(eventType: string) {
    const subscriptions: ? [EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any);
    if (subscriptions) {
      for (let i = 0, l = subscriptions.length; i < l; i++) {
        const subscription = subscriptions[i];

        // The subscription may have been removed during this event loop.
        if (subscription) {
          this._currentSubscription = subscription;
          subscription.listener.apply(
            subscription.context,
            Array.prototype.slice.call(arguments, 1)
          );
        }
      }
      this._currentSubscription = null;
    }
  }


これが、リクエストを開始したときのメッセージの処理方法です。もちろん、その過程でイベントを処理するコードが他にもいくつかありますので、ここではそのすべてを説明しませんが


DeviceEventEmitterはこうして使われる