React TypeError: "_this2.setState is not a function"?

Viewed 4251

I know this question is getting asked a lot and I did check most of the other answers provided but I still can't find out why I get this error.

Situation:

I have my textinput class in which I have a simple form for the user to input a name. When the user submits the button a REST call to my backend is called and the UI should show the user's name.

I have overridden the function componentDidMount of my App class to have an initial call to my backend when the page is loaded. This call works and I get the correct answer from my backend and the UI gets updated.

But when i make the call from my TextInput class, I get the error:

this2.setState is not a function

I believe that this is happening because I call the function from another class and the this state is not set correctly. I tried to bind all the things but that did not change anything. If anyone has an idea of what I am doing wrong, it would be very helpful!

I have the following classes:

import React, { Component } from 'react';
import logo from './logo.svg';
import Greeting from './components/greeting';
import TextInput from './components/textInput';
import './App.css';

const axios = require('axios');

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {name: "World"};
    this.getFormattedNameFromBackend.bind(this);
    this.setState.bind(this);
  }

  componentDidMount() {
    this.getFormattedNameFromBackend(this.state.name);
  }

  getFormattedNameFromBackend(name) {
    axios({
      method:'get',
      url:'http://localhost:8080/hello?name=' + name
    }).then((response) => {
      this.setState({ name : response.data.name});
    }).catch(function(error){
      console.log(error);
    });
  }


  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <Greeting data={this.state}/>
          <TextInput callBack = {this.getFormattedNameFromBackend}/>
        </header>
      </div>
    );
  }

}

export default App;

This is the main class where I get the error in the axios rest call.

the second class is this:

import React, { Component } from 'react';

export default class TextInput extends Component {
    constructor(props) {
      super(props);
      this.state = {value: ''};

      this.handleChange = this.handleChange.bind(this);
      this.handleSubmit = this.handleSubmit.bind(this);
    }

    handleChange(event) {
      this.setState({value: event.target.value});
    }

    handleSubmit(event) {
      this.props.callBack(this.state.value);

      event.preventDefault();
    }

    render() {
      return (
        <form onSubmit= {this.handleSubmit}>
          <label>
            Name:
            <input type="text" value={this.state.value} onChange={this.handleChange} />
          </label>
          <input type="submit" value="Submit" />
        </form>
      );
    }
  }

so the Question is, how do i call the correct this in the method getFormattedNameFromBackend?

Solved:

the mistake was setting the props of the Textinputfield wrong. Correctly it should be

<TextInput callBack = {(name)=>this.getFormattedNameFromBackend(name)}/>
2 Answers

In case anyone else reaches this question, https://reactjs.org/docs/handling-events.html explains quite well why this happens.

In short, this is not bound when event callbacks are invoked.

The easiest way to fix this is by enclosing this : onSubmit={(e) => {this.handleSubmit(e)} (altough this does create a new function instance each time, so need to be careful when using this method).

The way you solved it works by simply avoiding the binding if this altogether, but your original problem was that the line

    this.getFormattedNameFromBackend.bind(this);

doesn't do exactly what you think it does.
.bind() returns a new function that wraps the original function, but you're not doing anything with that new function. .bind() doesn't modify the original function so this.getFormattedNameFromBackend remains unbound and that's why <TextInput callBack = {this.getFormattedNameFromBackend}/> didn't work.

If you had written that line as:

    this.getFormattedNameFromBackend = this.getFormattedNameFromBackend.bind(this);

...(like you do in your TextInput class) it would get bound properly and callBack = {this.getFormattedNameFromBackend} would work.

Like I said, by writing it as callBack = {() => this.getFormattedNameFromBackend()} you're avoiding the binding issue anyway so you can simply remove this.getFormattedNameFromBackend.bind(this); (and BTW, this.setState.bind(this); is pretty pointless too)

Related