Tuesday, 20 September 2016

javascript - this.setState is not a function




I have the following component, which maintains the state that gets updated when the event is fired on the an specific element and when the state is updated it is passed down as a prop to another component. I am currently trying why i get the following error "this.setState is not a function", its most likely not binded to the right context. But i am unsure of this, am i doing this right?




export default class SearchBox extends Component{

constructor(){
super()
console.log("search box imported");
this.state = {
results:[]
};
}


//this.setState({result: arrayExample})

searchGif(event) {
if(event.keyCode == 13){
let inputVal = this.refs.query.value;
let xhr = new XMLHttpRequest();
xhr.open('GET', 'http://api.giphy.com/v1/gifs/search?q='+inputVal+'&api_key=dc6zaTOxFJmzC', true);
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status == 200){
// this.response = JSON.parse(xhr.responseText);

let returnedObj = JSON.parse(xhr.responseText);
//console.log(returnedObj.data);
let response = returnedObj.data.map(function(record){
let reformattedArray = { key: record.id,
id: record.id,
thumbnailUrl: record.images.fixed_height_small_still.url
};
return reformattedArray;
});
console.log(response);

this.setState({results: response});
}
}
xhr.send();
}
}


render(){


return(




);
}
}



EDIT:
I just realized the context gets changed when "onreadyStateChange" function, so i did the following in searchGif



searchGif(){
//other logic
var self = this;
xhr.onreadystatechange = function(){
//ajax logic
self.setState({results: repsonse});
}

}

Answer



You are losing the React class this context. Bind it, and also bind it in the async callback function too.



constructor(props){
super(props);
console.log("search box imported");
this.state = {
results:[]

};
this.searchGif = this.searchGif.bind(this);
}

searchGif(event) {
// ... code here
xhr.onreadystatechange = () => {
// ... code here
this.setState();
}

}


awesome thing about arrow functions is they bind your context for you and the syntax is awesome too. downside is browser support. Make sure you have a polyfil or a compile process to compile it into ES5 syntax for cross browser performance.



If you cant do either of those then just make a shadow variable of your this context outside of the async onreadystatechange function and use it instead of this.






Edit




Most compilers these days handle binding methods to the class with arrows (without specifying babel transforms ... etc), you can assign state as well this way without a constructor



export default class SearchBox extends Component {
state = {
results: []
}

searchGif = (event) => {
// ... code here

xhr.onreadystatechange = () => {
// ... code here
this.setState();
}
}
render() {
// ...
}
}


No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...