others-How to resolve the `Uncaught TypeError: Cannot read properties of undefined ` error in react.js applications

1. Purpose

In this post, I would demonstrate how to resovle the following error when I try to add a function to a react component:

Uncaught TypeError: Cannot read properties of undefined (reading 'updateCount')
    at onClickResetButton (Counter.js:29)
    at HTMLUnknownElement.boundFunc (ReactErrorUtils.js:63)
    at Object.ReactErrorUtils.invokeGuardedCallback (ReactErrorUtils.js:69)
    at executeDispatch (EventPluginUtils.js:83)
    at Object.executeDispatchesInOrder (EventPluginUtils.js:106)
    at executeDispatchesAndRelease (EventPluginHub.js:41)
    at executeDispatchesAndReleaseTopLevel (EventPluginHub.js:52)
    at Array.forEach (<anonymous>)
    at forEachAccumulated (forEachAccumulated.js:22)
    at Object.processEventQueue (EventPluginHub.js:252)

2. The solution

2.1 The origin code

Here is the code , I created a react component named Counter, user can click to increment or decrement the counter.

import React, { Component, PropTypes } from 'react';

const buttonStyle = {
  margin: '10px'
};

class Counter extends Component {

  constructor(props) {
    super(props);

    this.onClickIncrementButton = this.onClickIncrementButton.bind(this);
    this.onClickDecrementButton = this.onClickDecrementButton.bind(this);

    this.state = {
      count: props.initValue
    }
  }

  onClickIncrementButton() {
    this.updateCount(true);
  }

  onClickDecrementButton() {
    this.updateCount(false);
  }
  
  updateCount(isIncrement) {
    const previousValue = this.state.count;
    var newValue = isIncrement ? previousValue + 1 : previousValue - 1;

    this.setState({count: newValue})
    this.props.onUpdate(newValue, previousValue)
  }

  render() {
    const {caption} = this.props;
    return (
      <div>
        <button style={buttonStyle} onClick={this.onClickIncrementButton}>+</button>
        <button style={buttonStyle} onClick={this.onClickDecrementButton}>-</button>
        <span>{caption} count: {this.state.count}</span>
      </div>
    );
  }
}

Counter.propTypes = {
  caption: PropTypes.string.isRequired,
  initValue: PropTypes.number,
  onUpdate: PropTypes.func
};

Counter.defaultProps = {
  initValue: 0,
  onUpdate: f => f 
};

export default Counter;

Here is the result:

You can click + to add value, or click - to decrement value.

Now I want to add some features to it.

2.2 Change the code and the problem occurs

I want to add a reset button to this component, if user click reset button on the component, its value should be reset to 0.

Here is the code (Only changed parts included):

class Counter extends Component {
  ...

  onClickResetButton() {
    this.updateCount(false,true);
  }

  updateCount(isIncrement,isReset) { //add a parameter `isReset` to allow reset value 
    const previousValue = this.state.count;
    var newValue = isIncrement ? previousValue + 1 : previousValue - 1;
    if(isReset) { //If isReset is true, then reset the value
      newValue = 0;
    }

    this.setState({count: newValue})
    this.props.onUpdate(newValue, previousValue)
  }

  render() {
    const {caption} = this.props;
    return (
      <div>
        <button style={buttonStyle} onClick={this.onClickIncrementButton}>+</button>
        <button style={buttonStyle} onClick={this.onClickDecrementButton}>-</button>
        
        //add a button with a onClick prop to process the reset button onclick event
        <button style={buttonStyle} onClick={this.onClickResetButton}>0</button> 
        
        <span>{caption} count: {this.state.count}</span>
      </div>
    );
  }
}


You can see that I have changed the following lines:

  • Add a button to the render() function, to allow user click to reset. I passed a property to the button just like follows:

    onClick={this.onClickResetButton}
    
  • Then I add a function to process the onClick event like this:

    onClickResetButton() {
        this.updateCount(false,true);
      }
    
  • Then I changed the original function to reuse it process the reset event:

    updateCount(isIncrement,isReset) { //add a parameter `isReset` to allow reset value 
        const previousValue = this.state.count;
        var newValue = isIncrement ? previousValue + 1 : previousValue - 1;
        if(isReset) { //If isReset is true, then reset the value
          newValue = 0;
        }
      
        this.setState({count: newValue})
        this.props.onUpdate(newValue, previousValue)
      }
    

Now run the app:

$ npm run dev

I got this error :

Uncaught TypeError: Cannot read properties of undefined (reading 'updateCount')
    at onClickResetButton (Counter.js:29)
    at HTMLUnknownElement.boundFunc (ReactErrorUtils.js:63)
    at Object.ReactErrorUtils.invokeGuardedCallback (ReactErrorUtils.js:69)
    at executeDispatch (EventPluginUtils.js:83)
    at Object.executeDispatchesInOrder (EventPluginUtils.js:106)
    at executeDispatchesAndRelease (EventPluginHub.js:41)
    at executeDispatchesAndReleaseTopLevel (EventPluginHub.js:52)
    at Array.forEach (<anonymous>)
    at forEachAccumulated (forEachAccumulated.js:22)
    at Object.processEventQueue (EventPluginHub.js:252)

The core error message is:

Uncaught TypeError: Cannot read properties of undefined (reading 'updateCount')
at onClickResetButton (Counter.js:29)

The line 29 in Count.js is:

this.updateCount(false,true);

So the react.js compiler is complaining that it can not read updateCount from this, so I think the problem is caused by this.

2.3 The real solution #1

The problem is caused by the this reference, this can not be recoginized by default, except that you bind this to the function you declared in the component.

So the solution is very simple, just add this line to the constructor of you component:

this.onClickResetButton = this.onClickResetButton.bind(this);

So now the constructor of Counter component is as follows:

 constructor(props) {
    super(props);

    this.onClickIncrementButton = this.onClickIncrementButton.bind(this);
    this.onClickDecrementButton = this.onClickDecrementButton.bind(this);
    this.onClickResetButton = this.onClickResetButton.bind(this);

    this.state = {
      count: props.initValue
    }
  }

You can see that every function in the component that is calling this should bind this to allow the access.

2.4 The real solution #2

If you are tired of binding all functions to this ,you can just use arrow function to bind this automatically:

<button style={buttonStyle} onClick={()=>this.onClickResetButton()}>0</button>

If you choose this solution ,then the following line of code is NOT required, just comment it out:

//this.onClickResetButton = this.onClickResetButton.bind(this);

2.5 Run the app again

Now let’s run the react.js app again:

$ npm start

We got this:

It’s working!

3. Summary

In this post, I demonstrated how to solve the Uncaught TypeError: Cannot read properties of undefined (reading 'updateCount') error when trying to add a function to a react component, the key point is that you should remember to call bind(this) or use arrow function if you want to call this in your new function . That’s it, thanks for your reading.

-->