I'm just starting out in React and am building an app that incorporates a video search through Youtube's API. I am trying to dynamically render video data in my "search-results" div after querying the API for videos.
This "search-results" div is supposed to render an array of JSX, titled searchResults, that is stored as a state key-value pair of the AddSong component. Upon component construction, this array is empty, and it is supposed to be populated in the .then(function(response){} function of the axios get request (in my search function).
In this function, I am extracting relevant data from the API response item by item. For each item, I format data into JSX, and store it into a local array titled vidComponents. My problem arises when I call this.setState({searchResults: vidComponents}). 'This' is undefined. The exact error is stated below:
TypeError: Cannot read property 'setState' of undefined
After doing some research, I learned that most people get this error due to not binding 'this' to relevant functions correctly in the constructor, but this isn't the case for me. 'This' is bound to my search function in the constructor as follows:
this.search = this.search.bind(this)
Does anyone know what is causing this error? Here's my code:
import React, { Component } from 'react';
import axios from 'axios';
import { FormGroup, FormControl, Button } from 'react-bootstrap';
import './add-song-component.css';
class AddSong extends React.Component{
constructor(props, context){
super(props, context);
// Must bind this to functions in order for it to be accessible within them
this.handleTextChange = this.handleTextChange.bind(this);
this.search = this.search.bind(this);
// Object containing state variables
this.state = {
searchText: '',
searchResults: [],
nextPageToken: ''
};
}
// Keeps track of the current string in the search bar
handleTextChange(e){
this.setState({ searchText: e.target.value });
}
// Runs a search for 10 youtube videos through youtube's API
search(e){
// Initialize important data for formatting get request url
var api = this.props.apiKey;
var query = this.state.searchText;
var maxResults = 10;
// Execute get request on Youtube's search API
axios.get('https://www.googleapis.com/youtube/v3/search', {
params: {
part: 'snippet',
type: 'video',
order: 'viewCount',
key: api,
q: query,
maxResults: maxResults
}
})
.then(function (response) {
console.log(response.data);
var vidComponents = [];
// Loop through video data, extract relevant info
var i = 0;
for (let vidInfo of response.data.items) {
vidComponents.push(
<div key={i}>
<p>title: {vidInfo.snippet.title}</p>
<p>channel: {vidInfo.snippet.channelTitle}</p>
<p>id: {vidInfo.id.videoId}</p>
<p>thumbnailUrl: {vidInfo.snippet.thumbnails.high.url}</p>
</div>
);
i++;
}
// Set searchResults state to account for new results...
// Set nextPageToken state based on value from youtube's response
// *** used for retrieving more videos from initial query ***
this.setState({
searchResults: vidComponents,
nextPageToken: response.data.nextPageToken
});
})
.catch(function (error) {
console.log(error);
});
}
render(){
return(
<div className="add-song">
<form>
<FormGroup controlId="SearchText">
<FormControl
type="text"
placeholder="Search for a song / video"
value={this.state.searchText}
onChange={this.handleTextChange}
/>
</FormGroup>
<Button onClick={this.search}>Search</Button>
</form>
<div className="search-results">
{this.state.searchResults}
</div>
</div>
);
}
}
export default AddSong;
thisinside, like this:.then((response) => {....}thisin an Event refers to the Object to which the Event belongs. In other words, the Element.