问题
I have been struggling with trying to migrate my React code from ES5 to ES6. As I have found out by now, this
is no longer automatically bound which causes all sorts of hell.
I am trying to figure out by trial and error what objects are being passed around. So far I can find everything and adjust accordingly. However when it comes to this.setState I am having problems because it is not visible in console.log!!!! See screenshot in ES5:
and here is the same kind of code in ES6:
Please teach me how to fish i.e. help me figure out how to understand when an object has this.setState or not?
things i have tried
- from googling around i understand you might be able to default bind everything by changing the base component. unfortunately this did not work when it came to this.setState. It looks identical to the ES5 version of
this
in console so I concluded that setState is still not being bound somehow
回答1:
To oversimplify how this
works in JS:
- If you call a function as an object method (e.g.,
instance.foo()
) thenthis
refers to that object instance. - If you call a function by itself (e.g.,
foo()
), thenthis
is eitherundefined
or the global object, depending on whether strict mode is in effect. - If you take a reference to an object method then call it, that means you're calling it by itself, even though it was originally an object method. (E.g.,
var bar = instance.foo; bar();
.
Again, this is an oversimplification; MDN has details.
As this applies to React, and as explained in the React documentation on "Handling Events":
You have to be careful about the meaning of
this
in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bindthis.handleClick
and pass it toonClick
,this
will beundefined
when the function is actually called.
In your code, you render your RawInput
as
<RawInput value={this.state.value} updateValue={this.updateValue}/>
You're passing a reference updateValue
function in as a simple function, so this
will not be bound within updateValue
.
Basically, any time you pass a function as a React prop, unless you've bound it yourself, it's likely an error. The symptom is typically that this
is undefined. In your code, it's a little more complicated:
this.props.updateValue(modifiedValue);
The RawInput's updateValue
property is the unbound function App.updateValue
, but because you're invoking it as this.props.updateValue
, it's being called as if it were a method of this.props
- so this
refers to the RawInput's props
. That's why your console.log is showing an object with only two properties (start
and updateValue
): it isn't that setState
isn't bound or went away, it's that updateValue
wasn't bound, so this
isn't what you expect within updateValue
.
To fix the issue, as the React docs explain:
- Use a fat arrow function:
updateValue={(value) => this.updateValue(value)}
- Use the experimental property initializer syntax: Replace
updateValue(modifiedValue) {...}
withupdateValue = (modifiedValue) => {...}
. - Not mentioned in the React docs, but an approach I often use: Bind
updateValue
yourself. For example:
constructor(props) {
super(props);
this.updateValue = this.updateValue.bind(this);
}
回答2:
you can replace console.log with this:
console.shallowCloneLog = function(){
var typeString = Function.prototype.call.bind(Object.prototype.toString)
console.log.apply(console, Array.prototype.map.call(arguments, function(x){
switch (typeString(x).slice(8, -1)) {
case 'Number': case 'String': case 'Undefined': case 'Null': case 'Boolean': return x;
case 'Array': return x.slice();
default:
var out = Object.create(Object.getPrototypeOf(x));
out.constructor = x.constructor;
for (var key in x) {
out[key] = x[key];
}
Object.defineProperty(out, 'constructor', {value: x.constructor});
return out;
}
}));
}
any way, regarding your question, you can add a method like this:
updateValue = () => {...}
in m POV - es6 is cool and great. React components by es6' classes are useless. stick with createClass
and you'll be fine (and have mixins if you want!)
回答3:
Try Object.prototype.hasOwnProperty(). For example:
var X = function() {};
X.prototype.setSomething = 'a';
var x = new X();
x.setSomething; // log 'a' here
x.hasOwnPrperty('setSomething') // log false here
In your case, just console.log(this.hasOwnProperty('setState'))
.
回答4:
You have to bind your updateValue
function with the component in order to have the correct context (this
).
In your case, your parent class BaseComponent
allows you to use the herited method _bind
like that :
class App extends BaseComponent {
constructor(props){
super(props);
this.state={value:'start'};
this._bind('updateValue');
...
来源:https://stackoverflow.com/questions/41533177/how-can-i-tell-when-this-setstate-exists-in-es6