EventEmitter 和 Subscriber ES6 语法与 React Native

问题描述 投票:0回答:5

我正在尝试在 React Native 类中实现两个组件之间的 EventEmitter/Subscriber 关系。我看到参考了以下材料:

这些解决方案足以满足我想要完成的任务,但是,它们需要在接收组件上使用

mixins: [Subscribable.Mixin]
才能与
Subscriber
正常工作。不幸的是,我正在使用 ES6 并从
Component
扩展我的类,所以我不能使用这个 mixin 语法。

我的问题是:如何在不使用 mixins 的情况下在 ES6 中实现上述解决方案?

react-native mixins eventemitter
5个回答
37
投票

您不需要 mixin 来使用 EventEmitters。

简单演示:

import EventEmitter from 'EventEmitter';

let x = new EventEmitter();

function handler(arg) {
    console.log(`event-name has occurred! here is the event data arg=${JSON.stringify(arg)}`);
}

x.addListener('event-name', handler);

x.emit('event-name', { es6rules: true, mixinsAreLame: true });

addListener
的完整签名需要三个参数:

EventEmitter.addListener(eventName, handler, handlerContext)

在 React 组件中,您可能希望使用该上下文参数,以便处理程序可以是类方法而不是内联函数,并且仍然保留

this == component instance
。例如:

componentDidMount() {
    someEmitter.addListener('awesome', this.handleAwesomeEvents, this);
    // the generalist suggests the alternative:
    someEmitter.addListener('awesome', this.handleAwesomeEvents.bind(this));
}

handleAwesomeEvents = (event) => {
    let awesomeness = event.awesomeRating;

    // if you don't provide context in didMount,
    // "this" will not refer to the component,
    // and this next line will throw
    this.setState({ awesomeness });
};

仅供参考:我通过查看臭名昭著的订阅混合的绝对不神奇的实现得到了这一点。 Google 搜索结果基本上是 Ramsay 的基于 mixin 的单一演示的回声室。

附注至于将此发射器暴露给另一个组件,我可能会让拥有的组件提供一个用于接收发射器引用的函数,然后创建发射器的组件将有条件地使用发射器执行该道具。

// owner's render method:
<ThingThatEmits
    onEmitterReady={(emitter) => this.thingEmitter = emitter}
/>

// inside ThingThatEmits:
componentDidMount() {
    this.emitter = new EventEmitter();

    if(typeof this.props.onEmitterReady === 'function') {
        this.props.onEmitterReady(this.emitter);
    }
}

30
投票

这可能是一个很晚的答案,但我只是将其发布给任何可能觉得有用的人。

截至撰写本答案时(2020 年 7 月),React Native 自

0.60.0+
版本以来已经发生了很大变化,您可以使用
EventEmitter
的实例,或静态调用
DeviceEventEmitter
方法。


这是一个使用

EventEmitter
的示例:


import { EventEmitter } from 'events';

const newEvent = new EventEmitter();

// then you can use: "emit", "on", "once", and "off"
newEvent.on('example.event', () => {
  // ...
});


另一个使用

DeviceEventEmitter
的示例:


import { DeviceEventEmitter } from 'react-native';

// then you can directly use: "emit", "addListener", and "removeAllListeners"
DeviceEventEmitter.emit('example.event', ['foo', 'bar', 'baz']);

希望这对于那些仍在寻找在 React Native 中实现自定义事件的方法的人来说很方便。


1
投票

我能够通过 react-mixin 找到解决方法。不确定它是否正确,但无需任何修改即可运行。关键是在类定义后面添加

reactMixin(DetailView.prototype, Subscribable.Mixin);

继续使用 EventEmitter 和 Subscribable 的示例:

'use strict';

var reactMixin = require('react-mixin');
var React = require('react-native');
var EventEmitter = require('EventEmitter');
var Subscribable = require('Subscribable');

var {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    NavigatorIOS
} = React;

class MainView extends Component {
    constructor(props){
      super(props);
      this.EventEmitter = new EventEmitter();
    }

    somethingHappenedFunction(){
      this.EventEmitter.emit("update_event", { message: "hello from up here"});
    }

    //rest of the class
}

class DetailView extends Component {
   componentDidMount(){
     this.addListenerOn(this.props.events, 'update_event', this.miscFunction);
   }

   miscFunction(args) {
    console.log("message: %s", args.message);
   }

   //rest of the class
}
reactMixin(DetailView.prototype, Subscribable.Mixin);

0
投票

使用react-native 0.69.0我这样解决了它:

import EventEmitter from 'react-native/Libraries/vendor/emitter/EventEmitter';

const emitter = new EventEmitter();

emitter.addListener('event name', (...args) => console.log('emitted with', args));
emitter.emit('event name', { message: 'Foo' });

0
投票

在我看来,我认为不需要第三方库,在下面,您可以找到适用于任何 JavaScript/TypeScript 环境(如 Web、React Native 或 Node.js)的自定义解决方案:

const isFunction = (arg: any): boolean => typeof arg === 'function';
const isString = (arg: any): boolean => typeof arg === 'string';

type CallbackType = (data: unknown) => void;
type AddEventListenerReturnType = () => boolean;
type ListenersType = {
  count: number;
  refs: Record<string, {name: string; callback: CallbackType}>;
};

class EventRegister {
  private static _Listeners: ListenersType = {
    count: 0,
    refs: {},
  };

  public addEventListener(
    eventName: string,
    callback: CallbackType,
  ): AddEventListenerReturnType {
    if (isString(eventName) && isFunction(callback)) {
      EventRegister._Listeners.count++;
      const eventId = 'el' + EventRegister._Listeners.count;
      EventRegister._Listeners.refs[eventId] = {
        name: eventName,
        callback,
      };
      return this.removeEventListener.bind(this, eventId);
    }
    throw new Error('Event name must be a string, Callback must be a function');
  }

  public removeEventListener(eventId: string): boolean {
    if (isString(eventId)) {
      return delete EventRegister._Listeners.refs[eventId];
    }
    throw new Error('Event ID must be a string');
  }

  public removeAllListeners(): boolean {
    let removeError = false;
    Object.keys(EventRegister._Listeners.refs).forEach((_id) => {
      const removed = delete EventRegister._Listeners.refs[_id];
      removeError = !removeError ? !removed : removeError;
    });
    return !removeError;
  }

  public emitEvent(eventName: string, data: unknown): void {
    Object.keys(EventRegister._Listeners.refs).forEach((_id) => {
      if (
        EventRegister._Listeners.refs[_id] &&
        eventName === EventRegister._Listeners.refs[_id].name
      ) {
        EventRegister._Listeners.refs[_id].callback(data);
      }
    });
  }
}

const EventEmitter = new EventRegister();

export {EventEmitter};
© www.soinside.com 2019 - 2024. All rights reserved.