简体   繁体   中英

'this' context different for event handlers in function vs class react components

Why is 'this' (the context) different depending on whether I use a functional or class component in react? I understand how binding works and that my handler will take on the context of the onclick handler (or however it gets used in onclick) but I don't understand why the value is different for these 2 situations. The functional component prints the window object while the class component prints undefined.

How is the synthetic onclick event defined? What is its context? What does it do with its callback?

export default function App() {
  const handleClick = function () {
    console.log(this);
  };

  return (
    <div className="App">
      <button onClick={handleClick}>HandleClick Functional</button>
      <Toggle />
    </div>
  );
}

class Toggle extends React.Component {
  handleClick() {
    console.log(this);
  }

  render() {
    return <button onClick={this.handleClick}>HandleClick Class</button>;
  }
}

codesandbox link: https://codesandbox.io/s/onclick-context-test-ievog6?file=/src/App.js

TL;DR - This looks like a bug with CodeSandbox. Your React function component code ends up running in non-strict mode, which causes your this value to default to the global object instead of undefined as it should be.

This behaviour is pretty odd. But like qrsngky has correctly pointed out in the comments above, the issue is mainly to do with strict-mode and a result of how CodeSandbox does error handling. Below I've outlined my observations on why your code is behaving as it is.

Functions declared with the function keyword have their own value for this (their own "this binding"). How that value for this is determined is based on how the function is executed. Methods such as .call() and .apply() are able to set the this value for the function they execute.

In strict-mode code, using fn.apply(undefined) results in the function fn being executed and the this value within fn being set to undefined . However, in non-strict mode code, using fn.apply(undefined) results in the this value being set to theglobal object , which in browsers is the window object. When React invokes your onClick handler function ( func ), it invokes it by doing func.apply(context, ...) , with a context set to undefined (you can see this via a debugger):

在此处输入图像描述

At this point, you may be thinking that your code doesn't use the "use strict" directive anywhere, so it makes sense that when your function is called with a this of undefined it defaults to the global object window . However, it's not as straightforward as that;). While it may not look like any of your code is running in strict mode, it is actually classified as strict-mode code. That's because ES6 Modules (what you've written your react code in), automatically run in strict-mode . Similarly, classes also automatically run in strict-mode . So that begs the question as to why your code is showing window then if your code should actually be running in strict-mode.

When React code is written it needs to be transpiled into code that the browser can understand and execute. What seems to have occurred is that when your react code was transpiled, CodeSandbox has added a try-finally wrapper around your react code (you can see this using a debugger with source-maps disabled):

try {
  "use strict";
   ...
   function App() {
     const handleClick = function() {
       console.log(this);
     }
   }
...
} finally {
 ...
}

We can see that CodeSandbox has attempted to wrap your code within a try-finally block, where the try block runs in "strict mode" to try and match the same strict mode behavior automatically applied by the ES6 module your code is originally written in. However, applying "use strict" in a block {} that isn't associated with a function has no effect on the code's strictness (see here for rules on strict mode). As a result, the try-finally wrapper that CodeSandbox is applying ends up running your code in non-strict/sloppy mode. This causes this to be window when React calls your onClick handler with undefined .

On the other hand, your class still works as expected because in the transpiled code it remains as a class , so the strict-mode behavior around classes remains, and so you see undefined as expected.


When using React functional components, however, you shouldn't need to refer to this , and instead can use hooks such as useState() to manage things like state.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM