Note: This question is not asking how to bind the event handlers. It's asking why, without binding, this
is not consistent in the Button
's onClick
(it refers to an object) and the Form
's onSubmit
(it's undefined
).
Full question:
I was trying to check what happens to this
object inside a method if I don't bind the method inside the constructor. What I found is that the result is different for Form and Button. Below is my code for better understanding:
const {Button, Form, Input} = Reactstrap;
class Test extends React.Component{
constructor(props){
super(props);
}
handleClick(){
console.log(this); //This gives complete information about the Button object and its properties
}
handleSubmit(e){
console.log(this); //This one is undefined
e.preventDefault();
}
render(){
return(
<React.Fragment>
<Form onSubmit={this.handleSubmit} >
<Input type="submit" name="submit">Submit</Input>
</Form>
<Button name="butt1" onClick={this.handleClick}>Click</Button>
</React.Fragment>
);
}
}
ReactDOM.render(<Test />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reactstrap/8.4.1/reactstrap.min.js"></script>
I have checked that my question is different from this one because, in the provided question, the questioner wants to bind this
for the component while in my question, I want this
to refer to Form, just like it is referring to Button.
I am trying to find the reason for this different behavior, but I couldn't succeed. Can you people please give the reason for the same and suggest a way so that this
inside handleSubmit
starts referring to Form object?
EDIT This is what I believe the reason is, please confirm if it's correct
Since the handler for submitting was defined with the form, not for the submit button, this is why this
is undefined, because submit was clicked, not form. I think I need some bubbling capture like thing here.
It would appear to be a bug (or at least an inconsistency) in Reactstrap. With normal form
and button
elements, React consistently calls the handlers with no particular this
value¹ (so in this example, since class
code is always in strict mode, we see this
set to undefined
in the call):
class Test extends React.Component{
constructor(props){
super(props);
}
handleClick() {
console.log(typeof this);
}
handleSubmit(e) {
console.log(typeof this);
e.preventDefault();
}
render(){
return(
<React.Fragment>
<form onSubmit={this.handleSubmit} >
<input type="submit" name="submit" value="Submit" />
</form>
<button type="button" name="butt1" onClick={this.handleClick}>Click</button>
</React.Fragment>
);
}
}
ReactDOM.render(<Test />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
But I have read that
this
inside normal functions is resolved dynamically i.e. who called it.
That's incorrect. With normal functions and with methods, the value of this
within the call is determined by the caller. So what I'm saying above is that React does one thing and Reactstrap does something else.
You may be thinking of the DOM and how it handles event handlers, which is different from both React and (apparently) Reactstrap. The DOM calls your handler with this
set to the element the handler was attached to. So if you attach a handler to a button
and the handler is a normal function or a method, this
will refer to the button
when the handler is called by the DOM. It's just something the DOM does. React doesn't. Reactstrap apparently varies depending on what handler it is or what kind of element it is (which is likely a bug).
More about this
in this question's answers and in this old post on my anemic little blog.
In a comment you've said:
One more thing that I want to ask is how to use your code without strict mode, because it doesn't specify strict mode to be used
There are two reasons the code in that example is in strict mode:
class
construct. Code inside class
is always strict. (So is code in a JavaScript module.)So to see what happens in loose mode, you'd have to not use a class
(easy enough) and not use Babel, or at least tell Babel not to turn on strict mode. I don't know if there's a way to tell Babel not to use strict mode in Stack Snippets (there is a way when you're using it in the real world), but fortunately, you don't have to use JSX with React, it's just convenient. You can use React.createElement
directly:
const {createElement} = React;
function Test() {
function handleClick() {
console.log(this === window);
}
function handleSubmit(e) {
console.log(this === window);
e.preventDefault();
}
return createElement(React.Fragment, {
children: [
createElement("form", {
onSubmit: handleSubmit,
children: [
createElement("input", {
type: "submit",
name: "submit",
value: "Submit"
})
]
}),
createElement("button", {
type: "button",
name: "butt1",
onClick: handleClick,
children: ["Click"]
})
]
});
}
ReactDOM.render(createElement(Test), document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
Note that this
in the callbacks is now the global this
, because when you call a normal function or method with no particular this
value in loose mode, this
within the callback is the global this
(window
on browsers).
¹ That's even covered by React's event documentation, although what it actually says is that this
will be undefined
. That's only true in strict mode. It is, technically, possible — but not a good idea — to use React without using strict mode.