Reputation: 16908
Keep getting this error while trying to write an app working with 3rd party API
TypeError: this.setState is not a function
when I try to handle the API response. I suspect it's something wrong with this binding but I can't figure out how to fix it. Here's the code of my component:
var AppMain = React.createClass({
getInitialState: function() {
return{
FirstName: " "
};
},
componentDidMount:function(){
VK.init(function(){
console.info("API initialisation successful");
VK.api('users.get',{fields: 'photo_50'},function(data){
if(data.response){
this.setState({ //the error happens here
FirstName: data.response[0].first_name
});
console.info(this.state.FirstName);
}
});
}, function(){
console.info("API initialisation failed");
}, '5.34');
},
render:function(){
return (
<div className="appMain">
<Header />
</div>
);
}
});
Upvotes: 377
Views: 503988
Reputation: 5002
You can avoid the need for .bind(this) with an ES6 arrow function.
VK.api("users.get", { fields: "photo_50" }, (data) => {
if (data.response) {
this.setState({
//the error happens here
FirstName: data.response[0].first_name,
});
console.info(this.state.FirstName);
}
});
Upvotes: 158
Reputation: 2646
Here THIS context is getting changed. Use arrow function to keep context of React class.
VK.init(
() => {
console.info("API initialisation successful");
VK.api("users.get", { fields: "photo_50" }, (data) => {
if (data.response) {
this.setState({
//the error happens here
FirstName: data.response[0].first_name,
});
console.info(this.state.FirstName);
}
});
},
function () {
console.info("API initialisation failed");
},
"5.34"
);
Upvotes: 6
Reputation: 17
I Just Pass Props from child component to the parent component in react native from the child component is pass this.
const validate = (text) => {
console.log(text);
let reg = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w\w+)+$/;
if (reg.test(text) === false) {
console.log("Email is Not Correct");
({ onChangeText: text })
return false;
}
else {
// console.log("Email Correct");
props.onchangex(text)<<----------------------- here pass the value to the parent component
console.log(props.onchangex(text));
({ onChangeText: text })
}
and in parent component i set this value to the state like this
<InpE
onchangex={this.propx}<---------- pass the parent function to the child component
placeholder="Please Enter Email"
/>
propx=(a)=> {
this.setState({email:a}) <-------- this gives me error TypeError: this.setState is not a function
{this.state.email=a} <---------------- this line is setting up the state email variable
}
Upvotes: 0
Reputation: 1493
In my case, the problem was that I was sending the state and setstate as props to a child component but there was a typo in setstate
Upvotes: 2
Reputation: 994
I have the same error of
TypeError: setState is not a function
but the cause is silly. Posting it as a response here to hopefully save people who might be making the same mistake.
Instead of
const { state, setState } = React.useState(false);
Use
const [ state, setState ] = React.useState(false);
Square brackets and not curly brackets!
Upvotes: 8
Reputation: 11
This is mainly incompatibility problem between react, react-dom, and enzyme.
Try install the following as I did to solve the problem:
[...]
"react": "^18.0.0-beta-f320ef88f-20211116",
"react-dom": "16.14.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.6"
[...]
Upvotes: 1
Reputation: 181
use arrow functions, as arrow functions point to parent scope and this will be available. (substitute of bind technique)
Upvotes: 16
Reputation: 509
If you're doing this and still having an issue, my problem is I was calling two variables the same name.
I had companies
as an object brought in from Firebase, and then was trying to call this.setState({companies: companies})
- it wasn't working for obvious reasons.
Upvotes: 2
Reputation: 34014
You no need to assign this to a local variable if you use arrow function. Arrow functions takes binding automatically and you can stay away with scope related issues.
Below code explains how to use arrow function in different scenarios
componentDidMount = () => {
VK.init(() => {
console.info("API initialisation successful");
VK.api('users.get',{fields: 'photo_50'},(data) => {
if(data.response){
that.setState({ //this available here and you can do setState
FirstName: data.response[0].first_name
});
console.info(that.state.FirstName);
}
});
}, () => {
console.info("API initialisation failed");
}, '5.34');
},
Upvotes: 9
Reputation: 3388
React recommends bind this in all methods that needs to use this of class
instead this of self function
.
constructor(props) {
super(props)
this.onClick = this.onClick.bind(this)
}
onClick () {
this.setState({...})
}
Or you may to use arrow function
instead.
Upvotes: 63
Reputation: 674
You just need to bind your event
for ex-
// place this code to your constructor
this._handleDelete = this._handleDelete.bind(this);
// and your setState function will work perfectly
_handleDelete(id){
this.state.list.splice(id, 1);
this.setState({ list: this.state.list });
// this.setState({list: list});
}
Upvotes: 17
Reputation: 299
Now in react with es6/7 you can bind function to current context with arrow function like this, make request and resolve promises like this :
listMovies = async () => {
const request = await VK.api('users.get',{fields: 'photo_50'});
const data = await request.json()
if (data) {
this.setState({movies: data})
}
}
With this method you can easily call this function in the componentDidMount and wait the data before render your html in your render function.
I don't know the size of your project but I personally advise against using the current state of the component to manipulate datas. You should use external state like Redux or Flux or something else for that.
Upvotes: 7
Reputation: 132
Now ES6 have arrow function it really helpful if you really confuse with bind(this) expression you can try arrow function
This is how I do.
componentWillMount() {
ListApi.getList()
.then(JsonList => this.setState({ List: JsonList }));
}
//Above method equalent to this...
componentWillMount() {
ListApi.getList()
.then(function (JsonList) {
this.setState({ List: JsonList });
}.bind(this));
}
Upvotes: 10
Reputation: 2411
you could also save a reference to this
before you invoke the api
method:
componentDidMount:function(){
var that = this;
VK.init(function(){
console.info("API initialisation successful");
VK.api('users.get',{fields: 'photo_50'},function(data){
if(data.response){
that.setState({ //the error happens here
FirstName: data.response[0].first_name
});
console.info(that.state.FirstName);
}
});
}, function(){
console.info("API initialisation failed");
}, '5.34');
},
Upvotes: 41
Reputation: 67326
The callback is made in a different context. You need to bind
to this
in order to have access inside the callback:
VK.api('users.get',{fields: 'photo_50'},function(data){
if(data.response){
this.setState({ //the error happens here
FirstName: data.response[0].first_name
});
console.info(this.state.FirstName);
}
}.bind(this));
EDIT:
Looks like you have to bind both the init
and api
calls:
VK.init(function(){
console.info("API initialisation successful");
VK.api('users.get',{fields: 'photo_50'},function(data){
if(data.response){
this.setState({ //the error happens here
FirstName: data.response[0].first_name
});
console.info(this.state.FirstName);
}
}.bind(this));
}.bind(this), function(){
console.info("API initialisation failed");
}, '5.34');
Upvotes: 418