For some reason the value of this is being lost in react event handler. Reading the docs I thought that react did some stuff here to make sure this was set to the correct value
The following doesn't work as I'd expect
import React from 'react';
export default class Observer extends React.Component {
handleClick() {
console.log(this); //logs undefined
}
render() {
return (
<button onClick={this.handleClick}>Click</button>
);
}
}
But this does:
import React from 'react';
export default class Observer extends React.Component {
handleClick() {
console.log(this); //logs Observer class instance
}
render() {
return (
<button onClick={this.handleClick.bind(this)}>Click</button>
);
}
}
React and ES6 is new to me but this seems to not be the correct behaviour?
This is correct behavior for JavaScript and React if you use the new class syntax.
The autobinding feature does not apply to ES6 classes in v0.13.0.
So you'll need to use:
<button onClick={this.handleClick.bind(this)}>Click</button>
Or one of the other tricks:
export default class Observer extends React.Component {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
/* ... */
}
render() {
return <button onClick={this.handleClick}>Click</button>
}
}
The accepted answer is good and I've used it a lot in ES6, but I just want to add another "more modern" solution we have with ES7 (mentioned in the React component class auto-binding notes): use arrow functions as class properties, then you don't need to bind or wrap your handler anywhere.
export default class Observer extends React.Component {
handleClick = (e) => {
/* ... */
}
render() {
return <button onClick={this.handleClick}>Click</button>
}
}
This is the simplest and cleanest solution yet!
Like others have said, React doesn't autobind methods to the instance when using ES6 classes. That said, I would make habit of always using arrow functions in event handlers like: onClick={e => this.handleClick()}
Instead of: onClick={this.handleClick.bind(this)}
This because it means that you can replace the handleClick method with a spy in a test, something you can't do when you use bind.
Related
I have a question about ReactJS and components, specifically about how functions interact within the component system.
In this example:
// Index.js
import React from ‘/reactʼ;
import ReactDOM from ‘/react-domʼ;
import App from ‘./App.jsʼ;
ReactDOM.render(<App />, document.getElementById(‘rootʼ));
// App.js
import React from ‘/reactʼ;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {someProp = ‘ʼ};
};
functionA = (e) => { console.log(e);
};
Render() {
return <div><ComponentA /></div>
};
};
export default App;
// ComponentA.js
import React from ‘/reactʼ;
import App from ‘./../App.jsʼ;
class ComponentA extends React.Component {
constructor(props) {
super(props);
this.state = {someProp = ‘ʼ};
};
functionB = App.functionA
Render() {
return(
<div>
<input onSubmit={this.functionB} />
</div>
);
};
};
export default ComponentA;
ComponentA imports App.js and attempts to assign App.functionA to functionB and then call it in the JSX. This results in a failure basically saying that the function is not defined.
I know this is NOT the way to function share (I have learned about passing functions through props etc).
I simply just want to know WHY this does not work, to help me better understand the mechanics of React, and Javascript in general.
Thank you,
Curtis
To call a function from another React component, you can write static methods in ES6 notation. If you are using ES7, then you can also write static properties.
You can write statics inside ES6+ classes this way:
class Component extends React.Component {
static propTypes = {
...
}
static someMethod(){
}
}
Working Demo about static function
My noob brain finally figured it out lol... I think.
Basically because an instance of the class [the App component] was not initialized within the scope of ComponentA, the App function is not accessible.
This made it work (DISCLAIMER: I DO NOT PLAN ON DOING THIS, I KNOW ITS TERRIBLE CODE)
// ComponentA.js
import React from ‘/reactʼ;
import App from ‘./../App.jsʼ;
class ComponentA extends React.Component {
constructor(props) {
super(props);
this.state = {someProp = ‘ʼ};
this.appInstance = new App();
}
functionB = (e) => {
this.appInstance.functionA(e);
}
Render() {
return(
<div>
<input onSubmit={this.functionB} />
</div>
);
}
};
export default ComponentA;
Does class variables in a class need to be a part of the stateObject? I tried below with no luck. Here there is samples with simple variables so I am kind of surprice below does not work (alert says undefined)?
https://www.w3schools.com/react/react_es6.asp
https://codesandbox.io/s/jovial-glade-lfv4f?fontsize=14&hidenavigation=1&theme=dark
import React, { Component } from "react";
class Test extends Component {
constructor(props) {
super(props);
this.variable = "works";
}
clicked() {
alert(this.variable);
}
render() {
return <div onClick={this.clicked}>CLICK ME</div>;
}
}
export default Test;
You need to use bind() call to make it work.
constructor(props) {
super(props);
this.variable = "works";
this.clicked = this.clicked.bind(this);
}
for more information on this checkout Handling events in React
Why you have to bind here? so this is because you are using ES6 syntax for your components, and in ES6 class methods are not bound to classes by default, and to be able to use this keyword inside your methods and make it refer to the class instance you have bind your method to the class like in this answer.
import React, { Component } from "react";
class Test extends Component {
constructor(props) {
super(props);
this.variable = "works";
}
clicked = () => {
alert(this.variable);
}
render() {
return <div onClick={this.clicked}>CLICK ME</div>;
}
}
export default Test;
You can choose not to bind but you need to be adding fat-arrow function syntax in order to make it work.
I currently structure my React Components the following:
export default class Heading extends Component {
render() {
return (
<h1 onClick={changeLanguage("en_US")}>I am a Heading!</h1>
);
}
}
And now after some research I haven't found any way to define functions inside the Component Class that can be used inside the onClick attribute of something.
Everything I found was only for React.createClass or something like that.
You should be able to define the method inside of your component. You will need to wrap your event handler so you can pass an argument to it
export default class Heading extends Component {
changeLanguage(lang){
}
render() {
return (
<h1 onClick={()=>this.changeLanguage("en_US")}>I am a Heading!</h1>
)}
If the function is outside the render, you would refer to it like so:
export default class Heading extends Component {
changeLanguage = () => {
//do something
}
render() {
return (
<h1 onClick={()=>this.changeLanguage("en_US")}>I am a Heading!</h1>
);
}
}
changeLangue needs to be an arrow function to maintain context.
I have a basic React app and I'd like to put some commonly used functionality into a base component class and have all my other components inherit from that class to get access to those features. I have this:
export class BaseComponent extends React.Component {
constructor() {
super();
this.commonlyUsedMethod = this.commonlyUsedMethod.bind(this);
}
commonlyUsedMethod() {
let x = this.someValue; // <--- 'this' is undefined here
}
}
export class SomeComponent extends BaseComponent {
onButtonClick() {
super.commonlyUsedMethod();
}
render() {
return whatever;
}
}
The problem is that when I call super.commonlyUsedMethod() from the derived class, this.someValue blows up inside BaseComponent.commonlyUsedMethod() because this is undefined. I'm calling this.commonlyUsedMethod.bind(this); in the BaseComponent constructor, so I'm not sure what's going on.
First of all I (and most of the React dev community) don't recommend you to use inheritance. https://facebook.github.io/react/docs/composition-vs-inheritance.html
Most of the use cases you have you can solve it using Higher Order Components or writing functions in a JS file and importing it.
If you still want to go ahead and do this.
You need to bind the this when you attach the buttonClick listener
export class SomeComponent extends BaseComponent {
onButtonClick() {
super.commonlyUsedMethod();
}
render() {
return <div onClick={this.onButtonClick.bind(this)}>Hello</div>;
}
}
Here is the working example for it. https://www.webpackbin.com/bins/-Knp4X-n1RrHY1TIaBN-
Update: Problem was not with calling super with proper this, problem was with not binding proper this when attaching the onClick listener. Thanks #Mayank for pointing it out.
So I'm not sure if this a Good Practice™, but I can get it to work by calling this.someCommonMethod() instead of super.someCommonMethod(), like this:
export class SomeComponent extends BaseComponent {
constructor() {
super();
this.onButtonClick = this.onButtonClick.bind(this);
}
onButtonClick() {
this.commonlyUsedMethod(); <--- changed 'super' to 'this'
}
render() {
return whatever;
}
}
I'm new enough to React and ES6 not to know if this is how this should work. Any thoughts would be appreciated.
All I want to do is to add componentDidMount() method to original component. I don't want to change anything else in it. How do I extend it?
I need something like this:
import FooComponent from 'foo-component';
class MyComponent extends FooComponent {
componentDidMount() {
// my custom behavior
}
render() {
super()
}
}
module.exports = MyComponent;
You only have to rewrite the methods you want to change.
class MyComponent extends FooComponent {
componentDidMount() {
// my custom behavior
}
}
Example