Why do event handlers need to be references and not invocations?

前端 未结 2 1147
孤城傲影
孤城傲影 2020-12-16 06:37

In the React tutorial, it says

Doing onClick={alert(\'click\')} would alert immediately instead of when the button is clicked.

相关标签:
2条回答
  • 2020-12-16 07:03

    When you do onClick={alert("click")} that's going to call the alert function and assign the returned value (undefined) to the onClick property. So, what React sees is onClick={undefined} and says: well, that's not a function, why would I add such a handler?

    What you want to pass to onClick is a function, not undefined.

    Therefore, you have to do: onClick={myFunction} where myFunction can be () => alert("...") as you mentioned, or you can use bind to create a similar function:

    onClick={alert.bind(window, "click")}
    

    bind will return a new function which will internally call the alert function with the "click" argument.


    A similar case is when you do setTimeout(() => alert("after 1 second"), 1000). setTimeout expects a function. If you do setTimeout(alert("..."), 1000), the alert will be called, indeed, but the setTimeout will receive as first argument undefined (that's what alert returns).

    Instead, if you have a function which returns a function, that will work:

    // This will be called first and will return a function
    const sayHelloTo = name => {
       // This will be the function passed to `setTimeout`
       return () => alert(`Hello ${name}`);
    };
    setTimeout(sayHelloTo("Alice"), 1000);
    

    You can use it in the same way for the onClick thingy:

    onClick={sayHelloTo("Alice")}
    

    This is a very tiny example about what happens in the background (it's just a proof of concept, I'm sure what it actually happens is way better than this):

    const elm = {
      onClick: null,
      // The click method can be invoked manually
      // using `elm.click()` or is natively invoked by the browser
      click () {
         if (typeof this.onClick === "function") {
            this.onClick();
         }
      }
    };
    
    // If you do:
    elm.onClick = alert("click"); // this calls the alert, and returns undefined
    elm.onClick === undefined // true
    
    // But when doing:
    elm.onClick = () => alert("click");
    typeof elm.onClick // "function"
    
    elm.click(); // this will call the alert
    
    0 讨论(0)
  • 2020-12-16 07:05

    When you have an event handler it must be a function reference, not a function invocation. This is because internally when the handler is executed, the handler is first evaluated before it can be called. With inline JSX expressions such as when passing an event handler prop, the expression is evaluated when the component is declared, before the handler is called.

    Thus, if you pass in an invocation such as alert('click'), it will alert as it is being evaluated. When the event is triggered and the handler is called, it will then attempt to call the return value of alert (which is undefined) -- not good. Instead, you need a function reference, which when evaluated gives you a callable function which is then called as the handler.

    Take this pseudocode for an example:

    function on(event, handler) {
        //some event listener function that runs the handler when event happens
        //then we call handler when the event happens:
        handler();
    }
    

    Imagine if we invoked the function as on('click', alert('test')), then alert('test') would be evaluated and the return value would be passed as the handler, which is undefined. Then you would be trying to call undefined(), not good. Thus, you must pass a function reference, a reference to a callable function.

    0 讨论(0)
提交回复
热议问题