Reazioni.js eventi personalizzati per la comunicazione con i nodi principali


85

Sto creando e ascoltando normali DOM CustomEventper comunicare con i nodi principali:

Nel bambino:

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

In genitore:

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

Funziona, ma esiste un modo specifico per React che sarebbe migliore?


7
Il modo in cui React sarebbe passare i callback ai bambini esplicitamente tramite oggetti di scena - <Child onCustomEvent={this.handleCustomEvent} />. Non c'è supporto per eventi personalizzati con bolle in React.
andreypopp

16
Quindi, ridurre i callback anziché aumentare gli eventi? Sembra ragionevole.
forresto

23
@forresto ama il sarcasmo, +1
Dimitar Christoff

13
Non ero sarcastico.
forresto

5
una cosa è definire una buona pratica, un'altra è impedire un modello praticabile. in netto contrasto con twitter.github.io/flight , che utilizza DOMEvents per creare bolle e propagare eventi sintetici.
Dimitar Christoff

Risposte:


50

Come sopra annotato:

Il modo in cui React sarebbe passare i callback ai bambini esplicitamente tramite oggetti di scena -. Non c'è supporto per eventi personalizzati con bolle in React.

L'astrazione di programmazione reattiva è ortogonale:

La programmazione di sistemi interattivi mediante il modello di osservazione è difficile e soggetta a errori, ma è ancora lo standard di implementazione in molti ambienti di produzione. Presentiamo un approccio per deprecare gradualmente gli osservatori a favore di astrazioni di programmazione reattive. Diversi livelli di libreria aiutano i programmatori a migrare senza problemi il codice esistente dai callback a un modello di programmazione più dichiarativo.

La filosofia React si basa invece sul pattern Command:

inserisci qui la descrizione dell'immagine

Riferimenti


9
Sono interessato al motivo per cui non c'è supporto per eventi sintetici personalizzati in React. È semplicemente che non sono mai stati implementati o c'è una decisione di progettazione valida perché non lo sono stati? Gli eventi sono considerati dannosi come le istruzioni goto?
Michael Bylstra

5
Eventi @MichaelBylstra personalizzati sono disapprovate a causa di un problema classico : a class of debugging problems where you don't know what triggers some code because of a long chain of events triggering other events. Il modello di comando con flusso di dati unidirezionale è la filosofia di React.
Paul Sweatte

2
Considerando il fatto che i componenti comunicano con il Datastore iniettato da un antenato tramite il contesto React, (Redux, React Router, ecc. Usano tutti il ​​contesto), è totalmente falso dire che un bambino che richiama un antenato tramite qualsiasi funzione sul contesto non lo sia idiomatic React. Non vi è alcuna differenza effettiva tra chiamare Redux "invio" con un oggetto "azione" e chiamare un "gestore eventi" nel contesto con un oggetto "evento".
Andy,

2
Lunghe catene di eventi che innescano altri eventi sono abbastanza comuni nel modo in cui alcune persone usano Redux (ad esempio la follia di Redux-saga)
Andy

Ho capito, ma ho una domanda. Supponiamo che si crei una libreria UI-Kit che ha lo scopo di funzionare perfettamente su più architetture. Alcuni componenti potrebbero dover inviare eventi personalizzati perché non possiamo dare per scontato nulla su un'architettura sconosciuta. Il rigoroso presupposto fatto da React si traduce in un grosso problema. Puoi verificare il problema qui ( custom-elements-everywhere.com ): React è l'unica libreria che non può gestire correttamente eventi personalizzati. Forse mi manca qualcosa e ogni suggerimento sarebbe molto apprezzato.
7uc4

7

puoi scrivere un semplice servizio e poi usarlo

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

esempio (lo stesso per l'attivazione)

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');
  }
}

5

È possibile aumentare gli eventi tramite i callback trasmessi tramite i contesti: [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

Ce n'è un altro che ho trovato che è abbastanza ragionevole, soprattutto se praticare fori da genitore a figlio a figlio diventa già ingombrante. La chiamava comunicazione meno semplice. Ecco il link:

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


Questo in pratica sta dicendo "implementare il modello Observer". Sono d'accordo con il commento di Michael Bylstra sull'OP, che descrive il problema notato in Less Simple Communication come "perforazione" ... senza bolle, il passaggio di callback non gestisce strutture profonde> 1 livello.
ericsoco

3

Una possibile soluzione, se è assolutamente necessario ricorrere al pattern Observer in un ReactJs applicazione è possibile dirottare un evento normale. Ad esempio, se si desidera che la chiave di eliminazione provochi un elemento <div>contrassegnato per l'eliminazione, è possibile avere l' <div>ascolto di un evento keydown che verrà richiamato da un customEvent. Intrappola il keydown sul corpo e invia un customEventevento keydown sul selezionato <div>. Condividere nel caso in cui aiuti qualcuno.


3

Un archivio centrale [Redux] che distribuisce lo stato ai client, che poi "invia" lo stato di nuovo all'archivio è come un pattern di osservazione. Un modo per pubblicare / sottoscrivere è solo peggiore a causa del sovraccarico esplicito (fragile?) Che collega i percorsi di oggetti di scena / eventi. Per hackerare la gerarchia React fornisce il contesto (modello del provider) o librerie osservabili che puzzano. Come MobX che introduce nuovi decoratori @observable, o Vue che introduce la nuova sintassi del modello "v-if". Gli eventi sono comunque il modo principale in cui funzionano il ciclo di eventi DOM e javascript, quindi perché no? Penso che siano stati i satanisti. Lol


1

Mi rendo conto che questa domanda è piuttosto vecchia ormai, ma questa risposta potrebbe ancora aiutare qualcuno. Ho scritto un pragma JSX per React che aggiunge un evento personalizzato dichiarativo: jsx-native-events .

Fondamentalmente usi solo il onEvent<EventName>modello per guardare gli eventi.

<some-custom-element onEventSomeEvent={ callback }></some-custom-element>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.