React.js eventos personalizados para comunicarse con los nodos principales

84

Estoy creando y escuchando los DOM normales CustomEventpara comunicarse con los nodos principales:

En niño:

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

En padre:

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

Esto funciona, pero ¿hay una forma específica de React que sería mejor?

forresto
fuente
6
La forma de React sería pasar devoluciones de llamada a los niños explícitamente a través de accesorios - <Child onCustomEvent={this.handleCustomEvent} />. No hay soporte para eventos personalizados con burbujeo en React.
andreypopp
14
Entonces, ¿burbujear las devoluciones de llamada hacia abajo en lugar de los eventos hacia arriba? Parece razonable.
forresto
22
@forresto amo el sarcasmo, +1
Dimitar Christoff
12
No estaba siendo sarcástico.
forresto
5
Una cosa es establecer una práctica recomendada y otra totalmente distinta evitar un patrón viable. un contraste tan marcado para decir, twitter.github.io/flight , que usa DOMEvents para burbujear y propagar eventos sintéticos.
Dimitar Christoff

Respuestas:

47

Como se señaló anteriormente:

La forma de React sería pasar devoluciones de llamada a los niños explícitamente a través de accesorios -. No hay soporte para eventos personalizados con burbujeo en React.

La abstracción de la programación reactiva es ortogonal:

La programación de sistemas interactivos mediante el patrón de observador es difícil y propensa a errores, pero sigue siendo el estándar de implementación en muchos entornos de producción. Presentamos un enfoque para desaprobar gradualmente a los observadores a favor de abstracciones de programación reactiva. Varias capas de biblioteca ayudan a los programadores a migrar sin problemas el código existente de devoluciones de llamada a un modelo de programación más declarativo.

La filosofía de React se basa en el patrón Command en su lugar:

ingrese la descripción de la imagen aquí

Referencias

Paul Sweatte
fuente
8
Me interesa saber por qué no hay soporte para eventos sintéticos personalizados en React. ¿Es simplemente que nunca se implementaron, o hay una decisión de diseño válida por la que no se implementaron? ¿Los eventos se consideran dañinos como las declaraciones goto?
Michael Bylstra
4
@MichaelBylstra eventos personalizados son mal debido a un problema clásico : a class of debugging problems where you don't know what triggers some code because of a long chain of events triggering other events. El patrón de comando con flujo de datos unidireccional es la filosofía de React.
Paul Sweatte
2
Teniendo en cuenta el hecho de que los componentes se comunican con el almacén de datos inyectado por un antepasado a través del contexto React (Redux, React Router, etc., todos usan contexto), es totalmente falso decir que un niño que vuelve a llamar a un antepasado a través de cualquier función en el contexto no es Reaccionar idiomático. No hay una diferencia real entre llamar a Redux "despacho" con un objeto "acción" y llamar a un "controlador de eventos" en contexto con un objeto "evento".
Andy
1
Las cadenas largas de eventos que desencadenan otros eventos son bastante comunes en la forma en que algunas personas usan Redux (por ejemplo, la locura de la saga redux)
Andy
Entiendo el punto, pero tengo una pregunta. Supongamos que uno crearía una biblioteca UI-Kit que tiene como objetivo funcionar sin problemas en múltiples arquitecturas. Es posible que algunos componentes deban enviar eventos personalizados porque no podemos asumir nada sobre una arquitectura desconocida. La suposición estricta hecha por React resulta en un gran problema. Puede verificar el problema aquí ( custom-elements-everywhere.com ): React es la única biblioteca que no puede manejar eventos personalizados correctamente. Tal vez, me pierda algo y cualquier sugerencia sería muy apreciada.
7uc4
7

puedes escribir un servicio simple y luego 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];
  },
};

ejemplo (lo mismo para disparar)

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
fuente
4

Puede generar eventos a través de devoluciones de llamada transmitidas a través de contextos: [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
fuente
3

Hay otro que encontré que también es bastante razonable, especialmente si perforar agujeros de padre a hijo ya se vuelve engorroso. Lo llamó comunicación menos simple. Aquí está el enlace:

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

índice
fuente
Esto básicamente está diciendo "implementar el patrón Observer". Estoy de acuerdo con el comentario de Michael Bylstra sobre el OP, que describe el problema observado en Less Simple Communication como "perforar agujeros" ... sin burbujear, las devoluciones de llamada no manejan estructuras de> 1 nivel de profundidad.
ericsoco
3

Una posible solución, si absolutamente debe recurrir al patrón Observer en una aplicación ReactJs, puede secuestrar un evento normal. Por ejemplo, si desea que la clave de eliminación provoque un <div>marcado para su eliminación, puede hacer que <div>escuche un evento de keydown que será invocado por un evento personalizado. Atrapa el keydown en el cuerpo y envía un customEventevento keydown en el seleccionado <div>. Compartir en caso de que ayude a alguien.

Doug
fuente
3

Una tienda central [Redux] que distribuye el estado a los clientes, que luego el estado de 'despacho' de regreso a la tienda es como un patrón de observador también. Una forma de publicar / suscribirse solo es peor debido a la sobrecarga explícita (¿frágil?) Que conecta las rutas de accesorios / eventos. Para cortar la jerarquía, React proporciona contexto (patrón de proveedor) o bibliotecas observables que apestan. Como MobX que presenta nuevos decoradores @observable, o Vue que presenta una nueva sintaxis de plantilla "v-if". Los eventos son la forma principal en que el bucle de eventos DOM y JavaScript funciona de todos modos, así que ¿por qué no? Creo que los satanistas lo hicieron. Jajaja

user982671
fuente
1

Me doy cuenta de que esta pregunta ya es bastante antigua, pero esta respuesta aún podría ayudar a alguien. Escribí un pragma JSX para React que agrega un evento personalizado declarativo: jsx-native-events .

Básicamente, solo usa el onEvent<EventName>patrón para estar atento a los eventos.

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