question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Imperative API for installing DOM event handlers

See original GitHub issue

Currently React allows installing event handler on DOM events only in declarative fashion, via component attributes at the time of a component instantiation.

I think this is nice and allows automatically uninstall such handlers in case of a component disposal. But for implementing some “low-level” machinery I think it would be useful to allow handlers to be installed after the component instantiation at arbitrary moments. That would allow installing an event handler in a mixin’s componentDidMount (with a corresponding uninstall of such event handler in componentWillUnmount) callback.

For example currently I use jQuery for trapping clicks on anchors and invoking HTML5 pushState API instead of letting a browser to reload a page:

  ...
  componentDidMount: function() {
    $(this.getDOMNode()).on('click.react-route', 'a', function(e) {
      e.preventDefault();
      // invoke HTML5 pushState API
    });
  },

  componentWillUnmount: function() {
    $(this.getDOMNode()).off('click.react-route');
  }
 ...

The problem with that is I’m not able to use goodies provided by React’s event plugins. So what I want instead is to handle click events with React’s event system and install such handler in componentDidMount callback so I can extract such functionality into a mixin. Something like:

  ...
  handleClick: function(e) {
    // check if event's target is an anchor and invoke pushState API
  },

  componentDidMount: function() {
    this.addEventListener(eventTypes.onClick, this.handleClick);
  },

  componentWillUnmount: function() {
    this.removeEventListener(eventTypes.onClick, this.handleClick);
  }
 ...

Though maybe addEventListener and removeEventListener names are to verbose…

Other examples include handling focus/blur and arrow key clicks to control focus inside a component, …

Issue Analytics

  • State:closed
  • Created 10 years ago
  • Reactions:7
  • Comments:9 (5 by maintainers)

github_iconTop GitHub Comments

3reactions
eladocommented, May 11, 2016

This would be a great addition.

For example, if I want to handle triple click in a generic way, I have to wrap a component in another wrapper that would also output another element, like this <div>:

class TripleClickWrapper extends Component {
  render() {
    return <div onClick={this._onClick}>{this.props.children}</div>
  }

  _onClick = e => { /* counts clicks and handles timeouts etc and calls props.onTripleClick */ }
}

// somewhere else:

<TripleClickWrapper onTripleClick={::this._doSomething}>
  <SomeComponent />
</TripleClickWrapper>

I don’t want to use React.cloneElement(React.Children.only(this.props.children)) and inject a new onClick to props, because contained component might already have that event.

Currently the only solution to bind new events without a new element and without altering the nested component is by using regular DOM events, which, as stated by @andreypopp, doesn’t have all the goodies of React event system, something like that:

class TripleClickWrapper extends Component {
  componentDidMount() {
    // the DOM node actually refers whatever is in <SomeComponent>,
    // as this component doesn't have DOM nodes
    ReactDOM.findDOMNode(this).addEventListener('click', this._onClick, false)
  }

  componentWillUnmount() {
    ReactDOM.findDOMNode(this).removeEventListener('click', this._onClick)
  }

  render() {
    return React.Children.only(this.props.children)
  }

  _onClick = e => { /* counts clicks and handles timeouts etc and calls props.onTripleClick */ }
}

I’d imagine it as:

class TripleClickWrapper extends Component {
  componentDidMount() {
    this.addEvent('onClick', this._onClick) // will also auto-remove on unmount
  }

  render() {
    return React.Children.only(this.props.children)
  }

  _onClick = e => { /* counts clicks and handles timeouts etc and calls props.onTripleClick */ }
}
0reactions
gaearoncommented, Oct 1, 2017

Seems like this use case is very niche.

As noted above, a separate imperative API is unlikely. It seems like an intermediate component is the best solution for this use case.

The problems mentioned by @felipeochoa don’t seem related to this thread, so let’s track them in https://github.com/facebook/react/issues/285.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Handle and fire events - Polymer Project
Polymer elements can use the standard DOM APIs for creating, dispatching, and listening for events. Polymer also provides annotated event listeners, ...
Read more >
JavaScript, Events, DOM APIs - A-Frame
With JavaScript and DOM APIs, we can dynamically add and remove entities as we would with normal HTML elements. Creating an Entity with...
Read more >
How To Handle DOM and Window Events with React
In JavaScript apps using the React front-end library, you can use event handlers to update state data, trigger prop changes, ...
Read more >
LWC Events- simplified - sfdc techie – Pavan's blog
Events in Lightning web components are built on DOM Events. ... or programmatically using an imperative JavaScript API.
Read more >
react-event-listener - npm
A React component for binding events on the global scope. npm version npm downloads Build Status · Dependencies DevDependencies. Installation.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found