Last updated: Apr 6, 2024
Reading time·2 min
The "Cannot read property 'setState' of undefined" error occurs when a class
method is called without having the correct context bound to the this
keyword.
To solve the error, define the class method as an arrow function or use the
bind
method in the classes' constructor method.
Uncaught TypeError: Cannot read properties of undefined (reading 'setState')
Here is an example of how the error occurs.
import React, {Component} from 'react'; class App extends Component { constructor(props) { super(props); this.state = { isActive: false, }; } toggleIsActive() { // ⛔️ `this` is undefined here // Uncaught TypeError: Cannot read properties of undefined (reading 'setState') this.setState({isActive: !this.state.isActive}); } render() { console.log(this.state.isActive); return ( <div> <button onClick={this.toggleIsActive}>Toggle</button> </div> ); } } export default App;
Notice that we defined the toggleIsActive
method, but we haven't bound the
context of the this
keyword.
this
keyword in the toggleIsActive
method has a value of undefined
.To solve the error, switch the toggleIsActive
method to use an arrow function
instead.
import React, {Component} from 'react'; class App extends Component { constructor(props) { super(props); this.state = { isActive: false, }; } // ✅ Works because we used an arrow function // to declare the method toggleIsActive = () => { this.setState({isActive: !this.state.isActive}); }; render() { console.log(this.state.isActive); return ( <div> <button onClick={this.toggleIsActive}>Toggle</button> </div> ); } } export default App;
This works because arrow functions use the this
keyword of the enclosing
scope - in our example, the enclosing scope is the specific component instance.
Alternatively, you can call the Function.bind() method in the class's constructor.
import React, {Component} from 'react'; class App extends Component { constructor(props) { super(props); this.state = { isActive: false, }; // ✅ Bind method here this.toggleIsActive = this.toggleIsActive.bind(this); } // ✅ Works toggleIsActive() { // ✅ `this` is correctly bound here this.setState({isActive: !this.state.isActive}); } render() { console.log(this.state.isActive); return ( <div> <button onClick={this.toggleIsActive}>Toggle</button> </div> ); } } export default App;
The bind
method creates and returns a new function where the this
keyword is
set to the provided value.
The this
keyword in the constructor refers to the class instance.
This is the case when working in classes in vanilla JavaScript as well.
class Person { constructor(first, last) { this.first = first; this.last = last; console.log(this); // 👉️ {first: 'James', last: 'Doe'} } } const p1 = new Person('James', 'Doe');
This enables us to bind the method in the constructor
and use the bound
version throughout the class.
My preferred approach is to use arrow functions to define my class methods when
possible. This way I don't have to think about the this
keyword and everything
just works by default.