Preact: Always call event handlers with component as "this"

Created on 15 Jun 2018  路  8Comments  路  Source: preactjs/preact

Quite new to preact, but one thing struck my immediately. Wouldn't it be nicer if event handlers were called with the component in question as the thisArg automatically. Otherwise everybody needs to handle it everywhere with e.g. lambda bindings or explicit bind calls. E.g. something like

// Suggested code inside preact
const handler = listener.bind(compInstance);
el.addEventListener('someEvent', handler);

Maybe I'm missing something. Thanks for a nice product!

discussion question

Most helpful comment

Just a heads up, the value of this in event handlers is likely to change in an upcoming major version of Preact. Closing for now since it'll likely change to the element the handler was bound to (matching how addEventListener works).

All 8 comments

Thank you @staeke!

I guess what you talking about is a general JS issue, Preact does not at all change or modify any default JS behaviour or browser event behaviour.

So you are correct I guess, you have 2 ways to solve this:

el.addEventListener('someEvent', e => this.handler(e))

Or use babel transform class properties plugin

class myComp extends preact.Component {
   handler = (e) => {
    // your handle work
   }

   componentDidMount() {
      let el = ....
      el.addEventListener('someEvent', this.handler)
   }
}

Hope that answers your question?

Thanks for the answer @zouhir. I get that, but my question was more a suggestion for preact to run something like

const wrapper = evt => compList.call(component, evt);
el.addEventListener('someEvt', wrapper);

...i.e peovide the wrapper in preact so that people wouldn鈥檛 need to bother.

That's a cool suggestion! however we have planned for now to keep things as minimal, down to the metal and focused as possible.
I'll keep your suggestion on mind for the future tho!

Super glad you enjoying working with Preact & please keep the feedback coming our way! 馃榿

@zouhir. Yeah I鈥檓 super happy so far! We鈥檙e seeing performance gains of factor 10-50 compared to an ng5 app. Love it! And so cool with a 3kB footprint.

However - if people solve this problem for all their event handlers, their resulting code is for sure gonna be bigger than amount of added code you would put in by adding it once.

So if you don鈥檛 wanna add it in maybe you could provide an extension point so that we can add this ourselves.

While I kinda like this suggestion, I'm not sure it's worth it.

Because of how preacts diff and the vdom works, preact doesn't know "which component" a listener is related to.

The part attach the listeners doesn't even "know" about components:

https://github.com/developit/preact/blob/8be9c5f70ba8453fb3c262f38bbc852ee59afb64/src/dom/index.js#L95-L105

Also it would not be possible to "just" use the next component up in the vdom tree.
Example:

class SomeComponent extends Component {
    render({ children }) {
        return (
             <div>{children}</div>
        );
    }
}

class App extends Component {
   myListener() {
        // Hmm what exactly should be this ?
       console.log(this);
   }
   render() {
       return (
           <SomeComponent>
                  <button onClick={this.myListener}>What happens ? </button>
           </SomeComponent>
       );
   }
}

That would also make adding a hook hard.

A simple (while inefficent due to creating a new listener each render) module as a custom bind would be this:

function bind(component, listener) {
  return function (e) {
     listener.call(component, e);
  };
}

If used as a module with webpack (or another bundler ) it wouldn't be much bigger.

Otherwise I would recommend using arrow functions on the component class or to use something like decko s cached bind decorator.

Ah! Gotcha @Kanaye. Thanks for the thorough reply

I think we're going to see some movement in the component definition space soon - perhaps away from ES Classes once again, to simple factories that handle autobinding for you.

Personally, when I can get away with it, I prefer to use a decorator to declaratively bind methods to my component instances:

import { bind } from 'decko';

class Foo extends Component {
  @bind
  handleClick (e) {
    console.log(this);  // our Foo instance!
  }
  render () {
    return <button onClick={this.handleClick}>Click Me</button>
  }
}

Just a heads up, the value of this in event handlers is likely to change in an upcoming major version of Preact. Closing for now since it'll likely change to the element the handler was bound to (matching how addEventListener works).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

adriaanwm picture adriaanwm  路  3Comments

matthewmueller picture matthewmueller  路  3Comments

youngwind picture youngwind  路  3Comments

nopantsmonkey picture nopantsmonkey  路  3Comments

matuscongrady picture matuscongrady  路  3Comments