preact icon indicating copy to clipboard operation
preact copied to clipboard

Support for handleEvent DOM interface

Open kurtextrem opened this issue 5 years ago • 8 comments
trafficstars

Reproduction

class Foo {
  click(e) {
	this.setState({ clicked: true })
  }

  handleEvent(e) {
    // nicety: `this` is the component
    this[e.type](e)
  }
  render() {
    return <button onClick={this} />
  }
}

Steps to reproduce

Click the button.

Expected Behavior

handleEvent is used and doesn't throw an error.

Actual Behavior

this.l[e.type](P.event ? P.event(e) : e) throws an error, as this.l[e.type] is this.

kurtextrem avatar Mar 01 '20 15:03 kurtextrem

That's interesting! First time I've heard of it. If you can't wait, you can patch that into Preact via an option hook:

import { options } from "preact";

let oldVNode = options.vnode;
options.vnode = vnode => {
  const { type, props } = vnode;

  // Only check DOM nodes
  if (typeof type === "string") {
    for (const name in props) {
      const value = props[name];
      if (value && typeof value.handleEvent === "function") {
        props[name] = value.handleEvent;
      }
    }
  }
  if (oldVNode) oldVNode(vnode);
};

marvinhagemeister avatar Mar 01 '20 17:03 marvinhagemeister

Thank you for the quick reply! One note, wouldn't it be easier to check if the given event handler is this and then call it accordingly? Sadly, I don't understand Preact enough to help fix this - if you tell me how exactly are events added, or where, I could make prepare a PR.

Apart from that, I here is some imaginary code: "before": addEventListener(eventName, preactEventRouter, {opts}) "after": addEventListener(eventName, eventFn === this ? this : preactEventRouter, {opts})

I hope this shows my point.

(More about this here: https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38)

kurtextrem avatar Mar 01 '20 18:03 kurtextrem

The trick here is that we currently use a "proxy" handle in order to avoid constantly removing and re-adding handlers when developers use inline functions in render methods. If we reimplement support for handleEvent on top of function listeners, it loses all of the performance advantages.

But! I think there might be a way for us to switch to internally using handleEvent...

developit avatar Mar 01 '20 19:03 developit

Sounds reasonable and the but sounds interesting. Any help needed in exploring?

kurtextrem avatar Mar 01 '20 19:03 kurtextrem

Definitely! I can throw a PR together that should at least show the rough outline of what we are proposing here, then we can go from there?

developit avatar Mar 02 '20 13:03 developit

Sure, let's do this :)

kurtextrem avatar Mar 02 '20 13:03 kurtextrem

Just an update since I stumbled on this in my neverending list of tasks: I did get a chance to prototype full support for object event listener values in Preact, it just ends up being a size increase we'll need to find workarounds for:

https://gist.github.com/developit/bd824b90cd4c26db0d99576bd4737273

developit avatar Feb 02 '21 15:02 developit

Just for Reference:

February 2020 Andrea Giammarchi @WebReflection posted a workaround for using EventListener.handleEvent in React.

peerreynders avatar May 03 '21 20:05 peerreynders