I'm trying to acces a class property via 'this' in a callback function of an onClick event, but there is a scope-issue and the property isn't defined.
I've tried a couple of setups, but I think I'm just hitting myself in my confusion more than anything.
class MyClass extends React.Component {
constructor(props) {
super(props)
this.myProperty = "myValue"
}
onElementClicked = (event, other, parameters) => {
console.log(this.myProperty) //Undefined.
}
render() {
return (
<div>
<SubElement click={this.onElementClicked} />
</div>
)
}
}
function SubElement({ click }) {
const number = "Click me", other = null, parameters = null;
return (
<p>
<span onClick={e => click(e, other, parameters)}>
{number}
</span>
</p>
);
}
ReactDOM.render(
<MyClass />,
document.querySelector("main")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<main>Please enable JS</main>
You should not be using class fields and arrow functions for methods as they are attached to the object instance instead of the class prototype, which defeats prototypal inheritance.
Replace the arrow function with a regular function with the method definition syntax. Also, bind the function to this before passing it as prop, otherwise it will be "detached" from the class:
export default class MyClass {
constructor() {
this.myProperty = "myValue"
this.onElementClicked = this.onElementClicked.bind(this);
}
onElementClicked(event, other, parameters) {
console.log(this.myProperty) // Works.
}
render() {
ReactDOM.render(
<div>
<subElement click={this.onElementClicked}/>
</div>
)}
}
}
I am getting the following error
Uncaught TypeError: Cannot read property 'setState' of undefined
even after binding delta in the constructor.
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count : 1
};
this.delta.bind(this);
}
delta() {
this.setState({
count : this.state.count++
});
}
render() {
return (
<div>
<h1>{this.state.count}</h1>
<button onClick={this.delta}>+</button>
</div>
);
}
}
This is due to this.delta not being bound to this.
In order to bind set this.delta = this.delta.bind(this) in the constructor:
constructor(props) {
super(props);
this.state = {
count : 1
};
this.delta = this.delta.bind(this);
}
Currently, you are calling bind. But bind returns a bound function. You need to set the function to its bound value.
In ES7+ (ES2016) you can use the experimental function bind syntax operator :: to bind. It is a syntactic sugar and will do the same as Davin Tryon's answer.
You can then rewrite this.delta = this.delta.bind(this); to this.delta = ::this.delta;
For ES6+ (ES2015) you can also use the ES6+ arrow function (=>) to be able to use this.
delta = () => {
this.setState({
count : this.state.count + 1
});
}
Why ? From the Mozilla doc :
Until arrow functions, every new function defined its own this value [...]. This proved to be annoying with an object-oriented style of programming.
Arrow functions capture the this value of the enclosing context [...]
There is a difference of context between ES5 and ES6 class. So, there will be a little difference between the implementations as well.
Here is the ES5 version:
var Counter = React.createClass({
getInitialState: function() { return { count : 1 }; },
delta: function() {
this.setState({
count : this.state.count++
});
},
render: function() {
return (
<div>
<h1>{this.state.count}</h1>
<button onClick={this.delta}>+</button>
</div>
);
}
});
and here is the ES6 version:
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count : 1 };
}
delta() {
this.setState({
count : this.state.count++
});
}
render() {
return (
<div>
<h1>{this.state.count}</h1>
<button onClick={this.delta.bind(this)}>+</button>
</div>
);
}
}
Just be careful, beside the syntax difference in the class implementation, there is a difference in the event handler binding.
In the ES5 version, it's
<button onClick={this.delta}>+</button>
In the ES6 version, it's:
<button onClick={this.delta.bind(this)}>+</button>
You dont have to bind anything, Just use Arrow functions like this:
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 1
};
}
//ARROW FUNCTION
delta = () => {
this.setState({
count: this.state.count++
});
}
render() {
return (
<div>
<h1>{this.state.count}</h1>
<button onClick={this.delta}>+</button>
</div>
);
}
}
When using ES6 code in React always use arrow functions, because the this context is automatically binded with it
Use this:
(videos) => {
this.setState({ videos: videos });
console.log(this.state.videos);
};
instead of:
function(videos) {
this.setState({ videos: videos });
console.log(this.state.videos);
};
You have to bind your methods with 'this' (default object).
So whatever your function may be just bind that in the constructor.
constructor(props) {
super(props);
this.state = { checked:false };
this.handleChecked = this.handleChecked.bind(this);
}
handleChecked(){
this.setState({
checked: !(this.state.checked)
})
}
render(){
var msg;
if(this.state.checked){
msg = 'checked'
}
else{
msg = 'not checked'
}
return (
<div>
<input type='checkbox' defaultChecked = {this.state.checked} onChange = {this.handleChecked} />
<h3>This is {msg}</h3>
</div>
);
You can also use:
<button onClick={()=>this.delta()}>+</button>
Or:
<button onClick={event=>this.delta(event)}>+</button>
If you are passing some params..
if your are using ES5 syntax then you need to bind it properly
this.delta = this.delta.bind(this)
and if you are using ES6 and above you can use arrow function, then you don't need to use bind() it
delta = () => {
// do something
}
you have to bind new event with this keyword as i mention below...
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count : 1
};
this.delta = this.delta.bind(this);
}
delta() {
this.setState({
count : this.state.count++
});
}
render() {
return (
<div>
<h1>{this.state.count}</h1>
<button onClick={this.delta}>+</button>
</div>
);
}
}
You need to bind this to the constructor and remember that changes to constructor needs restarting the server. Or else, you will end with the same error.
This error can be resolved by various methods-
If you are using ES5 syntax, then as per React js Documentation you
have to use bind method.
Something like this for the above example:
this.delta = this.delta.bind(this)
If you are using ES6 syntax,then you need not use bind method,you can
do it with something like this:
delta=()=>{
this.setState({
count : this.state.count++
});
}
There are two solutions of this issue:
The first solution is add a constructor to your component and bind your function like bellow:
constructor(props) {
super(props);
...
this.delta = this.delta.bind(this);
}
So do this:
this.delta = this.delta.bind(this);
Instead of this:
this.delta.bind(this);
The second solution is to use an arrow function instead:
delta = () => {
this.setState({
count : this.state.count++
});
}
Actually arrow function DOES NOT bind it’s own this. Arrow Functions lexically bind their context so this actually refers to the originating context.
For more information about bind function:
Bind function
Understanding JavaScript Bind ()
For more information about arrow function:
Javascript ES6 — Arrow Functions and Lexical this
Arrow function could have make your life more easier to avoid binding this keyword. Like so:
delta = () => {
this.setState({
count : this.state.count++
});
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello World</title>
<script src="https://unpkg.com/react#0.14.8/dist/react.min.js"></script>
<script src="https://unpkg.com/react-dom#0.14.8/dist/react-dom.min.js"></script>
<script src="https://unpkg.com/babel-standalone#6.15.0/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
class App extends React.Component{
constructor(props){
super(props);
this.state = {
counter : 0,
isToggle: false
}
this.onEventHandler = this.onEventHandler.bind(this);
}
increment = ()=>{
this.setState({counter:this.state.counter + 1});
}
decrement= ()=>{
if(this.state.counter > 0 ){
this.setState({counter:this.state.counter - 1});
}else{
this.setState({counter:0});
}
}
// Either do it as onEventHandler = () => {} with binding with this // object.
onEventHandler(){
this.setState({isToggle:!this.state.isToggle})
alert('Hello');
}
render(){
return(
<div>
<button onClick={this.increment}> Increment </button>
<button onClick={this.decrement}> Decrement </button>
{this.state.counter}
<button onClick={this.onEventHandler}> {this.state.isToggle ? 'Hi':'Ajay'} </button>
</div>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('root'),
);
</script>
</body>
</html>
Just change your bind statement from what you have to
=>
this.delta = this.delta.bind(this);
Adding
onClick={this.delta.bind(this)}
will solve the problem .
this error comes when we try to call the function of ES6 class ,
So we need to bind the method.
though this question had a solution already, I just want to share mine to make it be cleared, hope it could help:
/*
* The root cause is method doesn't in the App's context
* so that it can't access other attributes of "this".
* Below are few ways to define App's method property
*/
class App extends React.Component {
constructor() {
this.sayHi = 'hello';
// create method inside constructor, context = this
this.method = ()=> { console.log(this.sayHi) };
// bind method1 in constructor into context 'this'
this.method1 = this.method.bind(this)
}
// method1 was defined here
method1() {
console.log(this.sayHi);
}
// create method property by arrow function. I recommend this.
method2 = () => {
console.log(this.sayHi);
}
render() {
//....
}
}
Check state
check state whether you create particular property or not
this.state = {
name: "",
email: ""
}
this.setState(() => ({
comments: comments //comments not available in state
}))
2.Check the (this)
if you doing setState inside any function (i.e handleChange) check whether the function bind to this or the function should be arrow function .
## 3 ways for binding this to the below function##
//3 ways for binding this to the below function
handleNameChange(e) {
this.setState(() => ({ name }))
}
// 1.Bind while callling function
onChange={this.handleNameChange.bind(this)}
//2.make it as arrow function
handleNameChange((e)=> {
this.setState(() => ({ name }))
})
//3.Bind in constuctor
constructor(props) {
super(props)
this.state = {
name: "",
email: ""
}
this.handleNameChange = this.handleNameChange.bind(this)
}
If using inside axios , Use the Arrow(=>) in then
axios.get('abc.com').then((response) => {});
If anyone is looking for the same sulution when using axios, or any fetch or get, and using setState will return this error.
What you need to do, is to define the component outside, as so:
componentDidMount(){
let currentComponent = this;
axios.post(url, Qs.stringify(data))
.then(function (response) {
let data = response.data;
currentComponent.setState({
notifications : data.notifications
})
})
}
I'm having hard time understanding why the text won't change in browser or why it won't even console.log the new state. I'm just trying to change the text by clicking on it.
class Komponentti extends React.Component{
constructor(props){
super(props)
this.state = {teksti: "Hello"}
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
this.setState = ({teksti: "Mello"}), function(){
console.log(this.state.teksti);
}
}
render(){
return(
<h1 onClick={this.handleClick}>{this.state.teksti}</h1>
)
}
}
You're calling it wrong. Should be:
handleClick() {
this.setState({teksti: "Mello"}), () => {
console.log(this.state.teksti);
}
}
May be you have been confused with es6 fat arrow functions.
In ES6 we can declare the functions using fat arrow notation to pass the lexical this to the function you declare.
Eg:
const example = () => {
// Something interesting
}
But we call that function as example().
But the setState() is an asynchronous function already declared in the React.
We can use it to update the state in the following manner.
handleClick(){
this.setState({teksti: "Mello"}), () => {
console.log(this.state.teksti);
}
}
Below are the way to set state :
this.setState({valuename:"value"});
this.state.varluename = "value";
this.forceUpdate();
I want to change the style of a component's element on some event, like a click. But when i do, i get the error that my style object is read-only when its not defined as such and thus immutable. Even defining the style object outside the class creates the same error. I created a simple example to illustrate my problem below.
Error:
Uncaught TypeError: Cannot assign to read only property 'color' of object '#<Object>'
profile.tsx
import * as React from 'react';
export default class Profile extends React.Component {
public styler = {
color:"white",
}
render() {
return (<button style={this.styler} onClick={this.HandleMenuClick.bind(this)}>Click me!</button>);
}
HandleMenuClick() {
this.styler.color = 'gray'; // error on executing this line
}
}
Error created upon clicking the button.
Same error is created when moving the style outside of the class:
var styler = {
color:"white",
}
You should use the this key word when inside a class. But to see changes in react you should use a state.
Here is a small example:
class Profile extends React.Component {
constructor(props) {
super(props);
this.state = {
styler:{
color: "white"
}
}
}
render() {
const {styler} = this.state;
return (
<button style={styler} onClick={this.HandleMenuClick}>
Click me!
</button>
);
}
HandleMenuClick = () => {
const { styler } = this.state;
this.setState({styler: {
...styler,
color: 'gray',
backgroundColor: '#000'
}})
};
}
ReactDOM.render(<Profile />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Edit
As a followup to your comment:
But out of curiosity, my this.styler.color variable never changes in
code, i know it wont call render but it doesnt even change the
variable in code and just gives the error, why is that?
As i mentioned above, you should use the this key word to attach objects to the class which basically just a sugar around the prototype pattern
JavaScript classes are introduced in ECMAScript 6 and are syntactical
sugar over JavaScript's existing prototype-based inheritance. The
class syntax is not introducing a new object-oriented inheritance
model to JavaScript. JavaScript classes provide a much simpler and
clearer syntax to create objects and deal with inheritance.
In your code you used the public key word which i'm not familiar with in this context (is it a typo?) + you should initialize objects inside the constructor.
here is an example with the object being mutated:
class Profile extends React.Component {
constructor(props) {
super(props);
this.myObject = {
myKey: "Hi there!"
};
}
render() {
return <button onClick={this.HandleMenuClick}>Click me!</button>;
}
HandleMenuClick = () => {
console.log(this.myObject);
this.myObject = {
myKey: "Bye there!"
};
console.log(this.myObject);
};
}
ReactDOM.render(<Profile />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Change your code as -
import * as React from 'react';
export default class Profile extends React.Component {
constructor()
{
super();
this.state.styler = {
color:"white"
}
}
render() {
return (<button style={this.styler} onClick={this.HandleMenuClick.bind(this)}>Click me!</button>);
}
HandleMenuClick() {
this.setState({'styler' : {'color':'gray'}});
}
}
I'm trying to follow the suggestion in this react-eslint doc to avoid using inline functions.
I have a div with an onClick funciton like so:
onClick={ () => this.props.handleClick(some_constant) }
This works perfectly fine, however I don't want to have an inline function. When I try to abstract it by following the pattern in the provided link above, I get a setState error that runs infinitely.
class Foo extends React.Component {
constructor() {
super();
this._handleClickWrapper = this.handleClickWrapper.bind(this);
}
_handleClickWrapper() {
// handleClick is a callback passed down from the parent component
this.props.handleClick(some_constant)
}
render() {
return (
<div onClick={this._handleClickWrapper}>
Hello!
</div>
);
}
}
What needs to be done so that I can avoid using inline functions?
Edit:
I made a serious typo, but in my code, I have what is currently reflected and it is still causing the error.
You bound the wrong function to this. It should be:
this._handleClickWrapper = this._handleClickWrapper.bind(this);
This way _handleClickWrapper will always be bound to the context of the component.
If you really really really want to follow the jsx-no-bind rule, you can create a new component and pass someConstant in as a prop. Then the component can call your callback with the value of someConstant:
class FooDiv extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
render() {
return <div onClick={this.handleClick}>Hello!</div>
}
handleClick() {
this.props.onClick(this.props.someConstant);
}
}
Then your Foo component can just do this:
class Foo extends React.Component {
render() {
const someConstant = ...;
return (
<FooDiv
onClick={this.props.handleClick}
someConstant={someConstant}
/>
);
}
}
Having said that, I would recommend not following jsx-no-bind and just use bind or arrow functions in render. If you're worried about performance due to re-renderings caused by using inline functions, check out the reflective-bind library.
There is a typo
this._handleClickWrapper = this.handleClickWrapper.bind(this);
should be
this._handleClickWrapper = this._handleClickWrapper.bind(this);
in your constructor you forgot to pass props to super()
constructor(props) {
super(props);
this._handleClickWrapper = this._handleClickWrapper.bind(this);
}
Tipp: You can avoid binding (and even the constructor) by using arrow functions declaration inside the class (babel-preset-es2016).
class Foo extends React.Component {
state = {} // if you need it..
onClick = () => {
this.props.handleClick(some_constant)
}
render() {
return (
<div onClick={this.onClick}>
Hello!
</div>
);
}
}
This way you components gets smaller, and easier to read.
https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding