親ノードと通信するためのreact.jsカスタムイベント


84

CustomEvent親ノードと通信するために、通常のDOMを作成してリッスンしています。

子供の場合:

  var moveEvent = new CustomEvent('the-graph-group-move', { 
    detail: {
      nodes: this.props.nodes,
      x: deltaX,
      y: deltaY
    },
    bubbles: true
  });
  this.getDOMNode().dispatchEvent(moveEvent);

親の場合:

componentDidMount: function () {
  this.getDOMNode().addEventListener("the-graph-group-move", this.moveGroup);
},

これは機能しますが、より良いReact固有の方法はありますか?


6
Reactの方法は、小道具を介して明示的にコールバックを子に渡すこと<Child onCustomEvent={this.handleCustomEvent} />です— 。Reactでのバブリングを伴うカスタムイベントのサポートはありません。
andreypopp 2014

14
では、イベントをアップする代わりに、コールバックをバブルダウンしますか?合理的なようです。
forresto 2014

22
@forrestoは皮肉が大好き、+ 1
Dimitar Christoff

12
私は皮肉ではありませんでした。
forresto 2014

5
これは、ベストプラクティスを設定することと、実行可能なパターンを防ぐための別のことです。言いたいことなど全く対照的、twitter.github.io/flight -バブルと伝播合成イベントにDOMEventsを使用しています。
Dimitar Christoff

回答:


47

上記のように:

Reactの方法は、小道具を介して明示的にコールバックを子に渡すことです—。Reactでのバブリングを伴うカスタムイベントのサポートはありません。

リアクティブプログラミングの抽象化は直交しています。

オブザーバーパターンを使用して対話型システムをプログラミングすることは難しく、エラーが発生しやすくなりますが、それでも多くの実稼働環境での実装標準です。リアクティブプログラミングの抽象化を支持して、オブザーバーを徐々に非推奨にするアプローチを提示します。いくつかのライブラリレイヤーは、プログラマーが既存のコードをコールバックからより宣言型のプログラミングモデルにスムーズに移行するのに役立ちます。

Reactの哲学は、代わりにコマンドパターンに基づいています。

ここに画像の説明を入力してください

参考文献


8
Reactでカスタム合成イベントがサポートされていない理由に興味があります。それらが実装されなかったというだけですか、それとも実装されなかった理由として有効な設計上の決定がありますか?イベントはgotoステートメントのように有害であると見なされますか?
Michael Bylstra 2015

4
@MichaelBylstraカスタムイベントは、古典的な問題のために嫌われています:a class of debugging problems where you don't know what triggers some code because of a long chain of events triggering other eventsコマンドパターン一方向のデータフローは哲学を反応します。
Paul Sweatte 2015

2
コンポーネントがReactコンテキストを介して祖先によって注入されたデータストアと通信するという事実を考えると(Redux、React Routerなどはすべてコンテキストを使用します)、コンテキスト上の関数を介して子が祖先にコールバックすると言うのは完全に偽物です慣用的な反応。「action」オブジェクトを使用してRedux「dispatch」を呼び出すことと、「event」オブジェクトを使用してコンテキストで「eventhandler」を呼び出すことの間に実際の違いはありません。
アンディ

1
他のイベントをトリガーするイベントの長いチェーンは、一部の人々がReduxを使用する方法で非常に一般的です(たとえば、redux-sagaの狂気)
Andy

要点はわかりますが、質問があります。複数のアーキテクチャ間でシームレスに動作することを目的としたUI-Kitライブラリを構築するとします。一部のコンポーネントは、不明なアーキテクチャについて何も想定できないため、カスタムイベントをディスパッチする必要がある場合があります。Reactによる厳密な仮定は、大きな問題を引き起こします。ここで問題を確認できます(custom-elements-everywhere.com):Reactは、カスタムイベントを適切に処理できない唯一のライブラリです。たぶん、私は何かが恋しいです、そしてどんな提案でも非常にありがたいです。
7uc4

7

簡単なサービスを書いてそれを使うことができます

/** eventsService */
module.exports = {
  callbacks: {},

  /**
   * @param {string} eventName
   * @param {*} data
   */
  triggerEvent(eventName, data = null) {
    if (this.callbacks[eventName]) {
      Object.keys(this.callbacks[eventName]).forEach((id) => {
        this.callbacks[eventName][id](data);
      });
    }
  },

  /**
   * @param {string} eventName name of event
   * @param {string} id callback identifier
   * @param {Function} callback
   */
  listenEvent(eventName, id, callback) {
    this.callbacks[eventName][id] = callback;
  },

  /**
   * @param {string} eventName name of event
   * @param {string} id callback identifier
   */
  unlistenEvent(eventName, id) {
    delete this.callbacks[eventName][id];
  },
};

例(トリガーについても同じ)

import eventsService from '../../../../services/events';
export default class FooterMenu extends Component {
  componentWillMount() {
    eventsService
      .listenEvent('cart', 'footer', this.cartUpdatedListener.bind(this));
  }

  componentWillUnmount() {
    eventsService
      .unlistenEvent('cart', 'footer');
  }

  cartUpdatedListener() {
    console.log('cart updated');
  }
}

4

コンテキストを介して渡されるコールバックを介してイベントをバブルアップできます:[CodePen]

import * as React from 'react';

const MyEventContext = React.createContext(() => {});

const MyEventBubbleContext = ({children, onMyEvent}) => {
  const bubbleEvent = React.useContext(MyEventContext);
  const handleMyEvent = React.useCallback((...args) => {
    // stop propagation if handler returns false
    if (onMyEvent(...args) !== false) {
      // bubble the event
      bubbleEvent(...args);
    }
  }, [onMyEvent]);
  return (
    <MyEventContext.Provider value={handleMyEvent}>
      {children}
    </MyEventContext.Provider>
  );
};

const MyComponent = () => (
  <MyEventBubbleContext onMyEvent={e => console.log('grandparent got event: ', e)}>
    <MyEventBubbleContext onMyEvent={e => console.log('parent got event: ', e)}>
      <MyEventContext.Consumer>
        {onMyEvent => <button onClick={onMyEvent}>Click me</button>}
      </MyEventContext.Consumer>
    </MyEventBubbleContext>
  </MyEventBubbleContext>
);

export default MyComponent;

3

私が見つけたもう1つは、特に親から子、子への穴あけがすでに面倒になっている場合にも、非常に合理的です。彼はそれをあまり単純なコミュニケーションとは呼びませんでした。リンクは次のとおりです。

https://github.com/ryanflorence/react-training/blob/gh-pages/lessons/04-less-simple-communication.md


これは基本的に「オブザーバーパターンを実装する」ということです。OPに関するMichaelBylstraのコメントに同意します。これは、Less Simple Communicationで指摘されている問題を「ドリルホール」として説明しています...バブリングせずに、コールバックを渡すと、1レベルを超える深層構造は処理されません。
ericsoco 2015年

3

考えられる解決策。ReactJsアプリでオブザーバーパターンに絶対に頼らなければならない場合は、通常のイベントを乗っ取ることができます。たとえば、削除キー<div>で削除のマークが付け<div>られたaを作成する場合は、customEventによって呼び出されるキーダウンイベントをリッスンすることができます。本体にキーダウンをトラップcustomEventし、選択したにキーダウンイベントをディスパッチし<div>ます。それが誰かを助ける場合に備えて共有する。


3

状態をクライアントに配布する中央ストア[Redux]は、状態をストアに戻す「ディスパッチ」もオブザーバーパターンのようなものです。小道具/イベントパスを接続する明示的な(脆弱な?)オーバーヘッドのために、パブリッシュ/サブスクライブを行う方法はさらに悪化します。階層をハックダウンするために、Reactはコンテキスト(プロバイダーパターン)または悪臭を放つ観察可能なライブラリを提供します。新しいデコレータ@observableを導入するMobXや、新しいテンプレート構文「v-if」を導入するVueのように。とにかく、イベントはDOMとjavascriptイベントループが機能する主要な方法です。私は悪魔主義者がそれをしたと思います。笑


1

この質問は今ではかなり古いと思いますが、この回答はまだ誰かを助けるかもしれません。宣言型のカスタムイベントを追加するReact用のJSXプラグマを作成しました:jsx-native-events

基本的には、onEvent<EventName>パターンを使用してイベントを監視します。

<some-custom-element onEventSomeEvent={ callback }></some-custom-element>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.