Ryan Cocuzzo
Ryan Cocuzzo

Reputation: 3219

Cannot read 'state' of undefined

I have been trying to figure out why this code doesn't bind to the class. In all cases of state properties not being defined that I have read, it was due to a lack of the following line:

this.someFunction = this.someFunction.bind(this);



But my code includes that line for my functions. I am, however, making an HTTP request with promises that I suspect are throwing off the compiler, despite the code being identical to the Axios example.

import React, { Component } from 'react';
import ReactDom from 'react-dom';
import logo from './logo.svg';
import './App.css';
import axios from 'axios';
import { Jumbotron } from "react-bootstrap";
import { Button } from "react-bootstrap";
import { Parallax, Background } from 'react-parallax';
import Fox from './fox.jpg';
var BASE_URL =  "http://127.0.0.1:8080/";
var BIN_API = BASE_URL + "binter-info";

class App extends Component {

  constructor(props) {
    super(props);

    this.state = {
      tradingFrom: '...',
      tradingTo: '...',
      result: '...'
    };
    this.retrieveValue = this.retrieveValue.bind(this);
    this.updateResult = this.updateResult.bind(this);
  }

  updateResult(str) {
    var self = this;
    if (str != null) {
      self.setState({
        result: str,
      });
    }
  }

  retrieveValue() {
    var self = this;
    console.log('App: retrieveValue executed...');
    let symbol = this.state.tradingTo + this.state.tradingFrom;
  //  Make Server Request
    axios.get(BIN_API, {
          params: {
              symbol: symbol,
          }
      }).then(function (response) {
    console.log("Good");
    //console.log(response);
    self.updateResult(response.data);
    console.log('this.state.result is now ' + this.state.result); // LINE 49
  }).catch(function (error) {
    console.log(error);         // LINE 51
    self.updateResult("ERROR");
    });
   }

  render() {
    return (
      <div className="App">
        <Parallax className='par'
      blur={6}
     bgImage={Fox}
      bgImageAlt="Fox"
      strength={200}
    >
      <h1>Welcome</h1>
      <div style={{ height: '200px' }} />
    </Parallax>
        <Jumbotron>
        <h2 className="App-Text">The following is a <strong>BETA_VERSION </strong>website.</h2>
        </Jumbotron>
        <Button className="App-Button" onClick={this.retrieveValue}> Retrieve Value </Button>
        <div className="TXTAREA">
          <p className="TA-p">{this.state.result}</p>
        </div>
        <Jumbotron>
        <h1 className="App-Text">but wait..</h1>
        </Jumbotron>
        <Jumbotron>
        <h1 className="App-Text">theres more!</h1>
        </Jumbotron>
      </div>
    );
  }
}

export default App;

My error is this:

App.js:51 TypeError: Cannot read property 'state' of undefined
    at App.js:49

Upvotes: 0

Views: 61

Answers (1)

Dan
Dan

Reputation: 10538

Your error is occuring because you are calling this.state inside a Promise callback from Axios. this will be undefined.

console.log('this.state.result is now ' + this.state.result);

Note that you can avoid this by using arrow functions rather than the function expression syntax you are using. This will bind this in the lexical context (lexical context meaning 'where the arrow function is defined' rather than 'where the function is called')

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      tradingFrom: "...",
      tradingTo: "...",
      result: "..."
    };
    this.retrieveValue = this.retrieveValue.bind(this);
  }

  updateResult(str) {
    var self = this;
    if (str != null) {
      self.setState({
        result: str
      });
    }
  }

  retrieveValue() {
    console.log("App: retrieveValue executed...");
    let symbol = this.state.tradingTo + this.state.tradingFrom;
    //  Make Server Request
    axios
      .get(BIN_API, {
        params: {
          symbol: symbol
        }
      })
      .then((response) => {
        this.updateResult(response.data);
        console.log("this.state.result is now " + this.state.result);
      })
      .catch((error) => {
        console.log(error);
        this.updateResult("ERROR");
      });
  }

  render() {
    return (
      <div className="App">
        <Button className="App-Button" onClick={this.retrieveValue}>
          {" "}Retrieve Value{" "}
        </Button>
        <div className="TXTAREA">
          <p className="TA-p">
            {this.state.result}
          </p>
        </div>
      </div>
    );
  }
}

Note that the pattern to bind functions in the constructor of a React component is only used because when you pass in a function like you do in the onClick handler:

onClick={this.retrieveValue}

You lose the this context when it is called. The ways to solve this are to use bind or arrow functions; the reason bind is used is because it avoids creating a new function object on each render call. So, with that said, you do not need to bind updateResult.

EDIT: Note that this code concerns a previous revision of the OPs code, but the issue is the same.

Upvotes: 2

Related Questions