问题
I have a method that calls an addListener to data that does not exist yet until you've clicked on a button. However, as I try to set that data inside a state, I get the following error:
TypeError: this.setState is not a function
I've tried binding my dataHandler like this:
this.dataHandler = this.dataHandler.bind(this)
yet it still returns this error. What am I forgetting here?
constructor(props){
super(props)
this.state = {
selActive: 4,
loaded: false,
mapInfo: ''
}
this.onScriptLoad = this.onScriptLoad.bind(this)
this.dataHandler = this.dataHandler.bind(this)
}
dataHandler = (getJson) => {
fetch(getJson)
.then(response => response.json())
.then(featureCollection =>
dataLayer = map.data.addGeoJson(featureCollection)
);
map.data.addListener('mouseover', function(event) {
map.data.revertStyle();
map.data.overrideStyle(event.feature, {fillColor: 'blue'});
// THIS IS WHERE I ADD MY SETSTATE.
// HOWEVER, THIS RETURNS AN ERROR ON HOVER
this.setState({mapInfo: event.feature.countryNL})
});
}
回答1:
In JavaScript this always refers to the "owner" of the function we're executing, or rather, to the object that a function is a method of.
Use arrow function instead.
An arrow functions doesn't have its own this/super/arguments binding. It inherits them from its parent lexical scope.
map.data.addListener('mouseover', (event) => {
map.data.revertStyle();
map.data.overrideStyle(event.feature, {fillColor: 'blue'});
// THIS IS WHERE I ADD MY SETSTATE.
// HOWEVER, THIS RETURNS AN ERROR ON HOVER
this.setState({mapInfo: event.feature.countryNL})
});
Reference:
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#Lexical_this
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
回答2:
this inside a normal function either refers to the global object or the current instance (in case of being invoked via new). However this inside an arrow function is captured from the outer scope. So this inside that addListener callback refers to the global object (window). To solve this problem you have the following options:
Using arrow function:
map.data.addListener('mouseover', (event) => { map.data.revertStyle(); map.data.overrideStyle(event.feature, {fillColor: 'blue'}); // THIS IS WHERE I ADD MY SETSTATE. // HOWEVER, THIS RETURNS AN ERROR ON HOVER this.setState({mapInfo: event.feature.countryNL}) });Using bound function:
map.data.addListener('mouseover', function (event) { map.data.revertStyle(); map.data.overrideStyle(event.feature, {fillColor: 'blue'}); // THIS IS WHERE I ADD MY SETSTATE. // HOWEVER, THIS RETURNS AN ERROR ON HOVER this.setState({mapInfo: event.feature.countryNL}) }.bind(this));
回答3:
First of all, I do not recommend you to use anonymous functions to be set as event listeners, because in this case you will not be able to removeEventListener or something simillar (probably, because it is not clear, what is the map here).
The second thing is that you should not bind your dataHandler, because it is already an arrow function, so there is no problems with this inside the dataHandler context. The thing that you should bind is your manual event listerer. So the result code should look like this:
constructor(props){
super(props)
this.state = {
selActive: 4,
loaded: false,
mapInfo: ''
}
this.onScriptLoad = this.onScriptLoad.bind(this)
this.dataHandler = this.dataHandler.bind(this)
this.handleMouseOver = this.handleMouseOver.bind(this)
}
dataHandler = (getJson) => {
fetch(getJson)
.then(response => response.json())
.then(featureCollection =>
dataLayer = map.data.addGeoJson(featureCollection)
);
map.data.addListener('mouseover', this.handleMouseOver);
}
handleMouseOver(event) {
map.data.revertStyle();
map.data.overrideStyle(event.feature, {fillColor: 'blue'});
this.setState({mapInfo: event.feature.countryNL})
}
or, using an arrow function, that is automatically being binded:
constructor(props){
super(props)
this.state = {
selActive: 4,
loaded: false,
mapInfo: ''
}
this.onScriptLoad = this.onScriptLoad.bind(this)
this.dataHandler = this.dataHandler.bind(this)
}
dataHandler = (getJson) => {
fetch(getJson)
.then(response => response.json())
.then(featureCollection =>
dataLayer = map.data.addGeoJson(featureCollection)
);
map.data.addListener('mouseover', this.handleMouseOver);
}
handleMouseOver = (event) => {
map.data.revertStyle();
map.data.overrideStyle(event.feature, {fillColor: 'blue'});
this.setState({mapInfo: event.feature.countryNL})
}
Hope, this will help you! :)
来源:https://stackoverflow.com/questions/54322518/react-how-to-use-setstate-in-addlistener