Question

I'm making and listening for normal DOM CustomEvents to communicate to parent nodes:

In child:

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

In parent:

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

This works, but is there a React-specific way that would be better?

No correct solution

OTHER TIPS

As noted above:

The React way would be to pass callbacks down to children explicitly via props — . There's no support for custom events w/ bubbling in React.

The reactive programming abstraction is orthogonal:

Programming interactive systems by means of the observer pattern is hard and error-prone yet is still the implementation standard in many production environments. We present an approach to gradually deprecate observers in favor of reactive programming abstractions. Several library layers help programmers to smoothly migrate existing code from callbacks to a more declarative programming model.

The React philosophy is based on the Command pattern instead:

enter image description here

References

you can write a simple service and then use it

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

example (same for triggering)

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

You could bubble events up through callbacks passed down via contexts: [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;

There is another one I found which is quite reasonable as well especially if drilling holes from parent to child to child becomes cumbersome already. He called it less simple communication. Here's the link:

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

A possible solution, if you absolutely must resort to the Observer pattern in a ReactJs app you can hijack a normal event. For example, if you want the delete key to cause a <div> that is marked for deletion, you could have the <div> listen for a keydown event which will be invoked by a customEvent. Trap the keydown on the body and dispatch a customEvent keydown event on the selected <div>. Sharing in case it helps someone.

I realize this question is quite old by now, but this answer might still help someone. I've written a JSX pragma for React that adds declarative custom event: jsx-native-events.

Basically you just use the onEvent<EventName> pattern to watch for events.

<some-custom-element onEventSomeEvent={ callback }></some-custom-element>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top