react.js custom events for communicating with parent nodes

Reactjs

Reactjs Problem Overview


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?

Reactjs Solutions


Solution 1 - Reactjs

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

Solution 2 - Reactjs

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

Solution 3 - Reactjs

You could bubble events up through callbacks passed down via contexts: [[CodePen]][1]

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;

[1]: https://codepen.io/jedwards1211/pen/zVxxEB "[CodePen]"

Solution 4 - Reactjs

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

Solution 5 - Reactjs

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.

Solution 6 - Reactjs

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>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionforrestoView Question on Stackoverflow
Solution 1 - ReactjsPaul SweatteView Answer on Stackoverflow
Solution 2 - ReactjsDr. Lemon TeaView Answer on Stackoverflow
Solution 3 - ReactjsAndyView Answer on Stackoverflow
Solution 4 - ReactjsindexView Answer on Stackoverflow
Solution 5 - ReactjsDougView Answer on Stackoverflow
Solution 6 - ReactjsCaleb WilliamsView Answer on Stackoverflow