0

I've found other posts with the similar errors, but those had functions which needed to be binded.

In the simple program below where I'm trying to update the DOM after an AJAX request is successful, I get the error ".TypeError: this.setState is not a function". Please help me understand why this code is not working.

import React from 'react';
import logo from './logo.svg';
import './App.css';


export class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            name: '(name will be inserted after ajax request)'
        };
        console.log('constructor');
    }


    componentDidMount() {
        //AJAX REQUEST
        const url = 'http://localhost:8080/ping';
        const xhr = new XMLHttpRequest();
        xhr.responseType = 'text';
        xhr.onreadystatechange = function() {
            if (xhr.readyState === XMLHttpRequest.DONE) {
                console.log(xhr.responseText);
                this.setState({name: xhr.responseText});
            }
        }
        xhr.open("GET", url);
        xhr.send();

    }



    render() {
        console.log('render')
        return <h1 > Hello {this.state.name} </h1>;
    }
}
jpj
  • 83
  • 1
  • 6

3 Answers3

0

Make sure you reference the right object or use an arrow function

componentDidMount() {
        //AJAX REQUEST
        var that = this;//make sure you reference the right object
        const url = 'http://localhost:8080/ping';
        const xhr = new XMLHttpRequest();
        xhr.responseType = 'text';
        xhr.onreadystatechange = function() {
            if (xhr.readyState === XMLHttpRequest.DONE) {
                console.log(xhr.responseText);
                that.setState({name: xhr.responseText});
            }
        }
        xhr.open("GET", url);
        xhr.send();

    }
Ishank
  • 2,722
  • 26
  • 43
  • 1
    While **not** *technically* wrong, this isn't the common pattern to use with current react, bordering on anti-pattern. – Drew Reese Aug 14 '20 at 16:34
  • I am trying to make sure that even in the absence of a build system that converts the code to ES5 from ES6 the code shall work in old browser and minimal changes to OPs existing code – Ishank Aug 14 '20 at 16:36
  • Wouldn't you need that anyway if you were using es6 classes? – Shadab Aug 14 '20 at 16:41
  • Right, mainly that is the reason I didn't downvote. It's *a* valid answer, just not an optimal solution. OP is using `XMLHttpRequest` so it's quite likely they are in an older codebase. I just wanted to comment as a sort of caution. – Drew Reese Aug 14 '20 at 16:42
  • any pointers why using that = this might be an anti-pattern? @DrewReese – Ishank Aug 14 '20 at 16:44
0

Try changing

xhr.onreadystatechange = function() {

to

xhr.onreadystatechange = () => {

Short explanation:

The this inside a function () {} depends on the object on which it's being called, so when you pass the function somewhere, you don't really know what this will refer to.

For arrow functions, this refers to the same this in the enclosing function i.e. the value for this is lexically resolved.

You can read a more detailed explanation here

Shadab
  • 1,051
  • 4
  • 9
0

Change the function to a arrow function or pass this as a param in function and then us this.setState({}) inside the function like this

  xhr.onreadystatechange = function(this) {
         if (xhr.readyState === XMLHttpRequest.DONE) {
            console.log(xhr.responseText);
            this.setState({name: xhr.responseText});
        }
    }
Mudit Gulgulia
  • 533
  • 2
  • 15