Reale.js benutzerdefinierte Ereignisse für die Kommunikation mit übergeordneten Knoten

84

Ich mache und höre auf normale DOMs CustomEvent, um mit übergeordneten Knoten zu kommunizieren:

Bei Kind:

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

Im Elternteil:

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

Das funktioniert, aber gibt es einen reaktionsspezifischen Weg, der besser wäre?

forresto
quelle
6
Der Weg der Reaktion wäre, Rückrufe explizit über Requisiten an Kinder weiterzuleiten <Child onCustomEvent={this.handleCustomEvent} />. Es gibt keine Unterstützung für benutzerdefinierte Ereignisse mit Sprudeln in React.
andreypopp
14
Sprudeln Sie also die Rückrufe nach unten, anstatt die Ereignisse nach oben? Scheint vernünftig.
Forresto
22
@ Forresto lieben den Sarkasmus, +1
Dimitar Christoff
12
Ich war nicht sarkastisch.
Forresto
5
Es ist eine Sache, eine Best Practice festzulegen, eine andere, um ein tragfähiges Muster zu verhindern. solch ein starker Kontrast zu sagen, twitter.github.io/flight - das die DOMEvents verwendet, um synthetische Ereignisse zu sprudeln und zu verbreiten.
Dimitar Christoff

Antworten:

46

Wie oben beschrieben:

Der Weg der Reaktion wäre, Rückrufe explizit über Requisiten an Kinder weiterzuleiten. Es gibt keine Unterstützung für benutzerdefinierte Ereignisse mit Sprudeln in React.

Die reaktive Programmierabstraktion ist orthogonal:

Die Programmierung interaktiver Systeme mithilfe des Beobachtermusters ist schwierig und fehleranfällig, in vielen Produktionsumgebungen jedoch immer noch der Implementierungsstandard. Wir präsentieren einen Ansatz, um Beobachter schrittweise zugunsten reaktiver Programmierabstraktionen abzulehnen. Mehrere Bibliotheksebenen helfen Programmierern, vorhandenen Code reibungslos von Rückrufen auf ein deklarativeres Programmiermodell zu migrieren.

Die React-Philosophie basiert stattdessen auf dem Befehlsmuster:

Geben Sie hier die Bildbeschreibung ein

Verweise

Paul Sweatte
quelle
8
Mich interessiert, warum es in React keine Unterstützung für benutzerdefinierte synthetische Ereignisse gibt. Ist es einfach so, dass sie nie implementiert wurden, oder gibt es eine gültige Entwurfsentscheidung, warum sie nicht implementiert wurden? Werden Ereignisse als schädlich angesehen, wie etwa goto-Aussagen?
Michael Bylstra
4
@MichaelBylstra Benutzerdefinierte Ereignisse werden aufgrund eines klassischen Problems verpönt : a class of debugging problems where you don't know what triggers some code because of a long chain of events triggering other events. Die Befehlsmuster mit Einbahndatenfluss sind die Philosophie React.
Paul Sweatte
2
Angesichts der Tatsache, dass Komponenten mit dem Datenspeicher kommunizieren, der von einem Vorfahren über den React-Kontext injiziert wurde (Redux, React Router usw. verwenden alle den Kontext), ist es völlig falsch zu sagen, dass ein Kind, das über eine beliebige Funktion im Kontext zu einem Vorfahren zurückruft, dies nicht ist idiomatische Reaktion. Es gibt keinen tatsächlichen Unterschied zwischen dem Aufruf von Redux "dispatch" mit einem "action" -Objekt und dem Aufruf eines "event handler" im Kontext mit einem "event" -Objekt.
Andy
1
Lange Ketten von Ereignissen, die andere Ereignisse auslösen, sind in der Art und Weise, wie manche Leute Redux verwenden (z. B. der Redux-Saga-Wahnsinn), ziemlich häufig
Andy
Ich verstehe, aber ich habe eine Frage. Nehmen wir an, man würde eine UI-Kit-Bibliothek erstellen, die nahtlos über mehrere Architekturen hinweg funktionieren soll. Einige Komponenten müssen möglicherweise benutzerdefinierte Ereignisse auslösen, da wir nichts von einer unbekannten Architektur annehmen können. Die strikte Annahme von React führt zu einem großen Problem. Sie können das Problem hier überprüfen ( custom-elements-everywhere.com ): React ist die einzige Bibliothek, die benutzerdefinierte Ereignisse nicht ordnungsgemäß verarbeiten kann. Vielleicht vermisse ich etwas und jeder Vorschlag wäre sehr dankbar.
7uc4
7

Sie können einen einfachen Dienst schreiben und ihn dann verwenden

/** 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];
  },
};

Beispiel (dasselbe zum Auslösen)

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');
  }
}
Ivan Maslov
quelle
4

Sie können Ereignisse durch Rückrufe in die Luft sprudeln lassen, die über Kontexte weitergegeben werden: [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;
Andy
quelle
3

Es gibt noch eine andere, die ich gefunden habe und die auch ziemlich vernünftig ist, besonders wenn das Bohren von Löchern von Eltern zu Kind zu Kind bereits umständlich wird. Er nannte es weniger einfache Kommunikation. Hier ist der Link:

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

Index
quelle
Dies bedeutet im Grunde "Implementieren des Beobachtermusters". Ich stimme dem Kommentar von Michael Bylstra zum OP zu, in dem das in Less Simple Communication festgestellte Problem als "Bohren von Löchern" beschrieben wird.
Ericsoco
3

Eine mögliche Lösung, wenn Sie unbedingt müssen auf die Beobachter - Muster in einem ReactJs greifen app Sie ein normales Ereignis kapern kann. Wenn Sie beispielsweise möchten <div>, dass der <div>Löschschlüssel ein zum Löschen markiertes Schlüssel verursacht , können Sie auf ein Keydown-Ereignis warten, das von einem benutzerdefinierten Ereignis aufgerufen wird. Fangen Sie den Keydown am Körper ein und customEventsenden Sie ein Keydown-Ereignis an den ausgewählten <div>. Teilen, falls es jemandem hilft.

Doug
quelle
3

Ein zentrales Geschäft [Redux], das den Status an Kunden verteilt und dann den Status wieder an das Geschäft sendet, ist ebenfalls wie ein Beobachtermuster. Eine Möglichkeit zum Veröffentlichen / Abonnieren ist nur aufgrund des expliziten (spröden?) Overheads beim Verbinden von Requisiten / Ereignispfaden schlechter. Durch Hierarchie hacken React bietet Kontext (Anbietermuster) oder beobachtbare Bibliotheken, die stinken. Wie MobX, das neue Dekoratoren @observable einführt, oder Vue, das die neue Vorlagensyntax "v-if" einführt. Ereignisse sind sowieso die Hauptmethode, mit der DOM- und Javascript-Ereignisschleifen funktionieren. Warum also nicht? Ich denke, die Satanisten haben es getan. Lol

user982671
quelle
1

Mir ist klar, dass diese Frage mittlerweile ziemlich alt ist, aber diese Antwort könnte immer noch jemandem helfen. Ich habe ein JSX-Pragma für React geschrieben, das deklaratives benutzerdefiniertes Ereignis hinzufügt: jsx-native-events .

Grundsätzlich verwenden Sie das onEvent<EventName>Muster nur, um nach Ereignissen zu suchen.

<some-custom-element onEventSomeEvent={ callback }></some-custom-element>
Caleb Williams
quelle