Cannot read property 'setState' of undefined

import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
import videoDetails from './components/listvideos';

class YoutubeApp extends Component {
    constructor(props){
        super(props)

    this.state = {
          videos:[]
    };

    this.apiCall('surfing');
    
   
}
    apiCall(term){
        const params = {
            part: 'snippet',
            key:APP_KEY,
            q:term,
            type: 'video'
        };
    axios.get(APP_URL, { params: params})
        .then(function(response) {
           this.setState({
               videos:response
           })
        })
        .catch(function(error) {
        console.error(error);
      });
   }

    render(){
        
        return (
            <div>
            <videoDetails/>
            </div>
        )
    }


}


ReactDOM.render(
    <YoutubeApp/>,
    document.getElementById('app')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="app">
</div>

Hi I am trying to make a youtube clone for practice I am a beginner on this but i cannot see why the state on react is giving me a problem
I am trying to set the state to my videos array so i can pass it to other components I didn’t include the api key or url but is very readable by the way this is the error
{Cannot read property ‘setState’ of undefined}

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

You’re using function() in your Promise chain, this will change the scope for this. If you’re using ES6 I suggest you use the arrow function to maintain the class scope. Example:

You’re using

axios.get(APP_URL, { params: params})
    .then(function(response) { // this resets the scope
       this.setState({
           videos:response
       })
    })

Try using arrow functions:

axios.get(APP_URL, { params: params})
    .then((response) => {
       this.setState({
           videos:response
       })
    })

Solution 2

You also may need to add this.apiCall = this.apiCall.bind(this) before the this.apiCall('surfing'); on your constructor method. This will bind the scope of apiCall to the scope of the react component.

More here: https://facebook.github.io/react/docs/handling-events.html

Also, remove this.apiCall('surfing'); from the constructor and put it into a componentDidMount function, the constructor should not be used for execution.

Solution 3

It is due to scoping in your API call. The value of this is different within your response callback in your promise chain. There are two ways to solve this.

You could set a variable referencing this at the beginning of your apiCall method.

apiCall(term){

    const that = this;

    const params = {
        part: 'snippet',
        key:APP_KEY,
        q:term,
        type: 'video'
    };

   axios.get(APP_URL, { params: params}).then(function(response) {
       that.setState({
           videos:response
       });
    }).catch(function(error) {
       console.error(error);
  });

}

or else you can use ES6 arrow functions, which maintain the original scope, like this:

axios.get(APP_URL, { params: params}).then(response => {
    this.setState({
         videos:response
    });
})

Solution 4

You can assign the current context this to a variable, also be consistent either use ES5 or ES6.

apiCall(term) {
  const params = {
    part: 'snippet',
    key: APP_KEY,
    q: term,
    type: 'video'
  };
  const _this = this;

  axios.get(APP_URL, { params: params})
   .then((response) => {
     _this.setState({
       videos: response
     });
   })
   .catch((error) => {
    console.error(error);
   });
}

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply