我具有以下嵌套的类结构:
import React, {Component} from 'react';
import {TextField} from '@material-ui/core';
import './ProfileEditor.css';
export default class ProfileEditor extends Component {
SyncedTextField = class SyncedTextField extends Component {
onChange = event => {
console.log(this);
};
render() {
return (
<TextField
{...this.props}
onChange={this.onChange}/>
);
}
};
render() {
return (
<form className={"ProfileEditor"}>
<this.SyncedTextField/>
</form>
);
}
}
当代码被Webpack捆绑并在Firefox中运行时,它可以正确运行this.onChange
,但是输出的this
则引用ProfileEditor
类的上下文。
这太奇怪了,因为在JSX中,当我引用“ this”时,它正确指向SyncedTextField,但是在onChange方法中,它指向ProfileEditor
。
我确实向ProfileEditor
添加了一些属性,以进行自我检查,即使在SyncedTextField中提供了冲突的定义,这些属性也会按照ProfileEditor
中的声明显示。
有人可以告诉我如何避免这个问题,以及可能是什么原因造成的吗?
答案 0 :(得分:2)
不正确的行为可能是特定于浏览器开发工具的。但是在这种情况下,这是由编译器的工作方式引起的。 Babel 6类字段(属于第3阶段建议)转换实现中存在一个错误。
The example使用Babel编译,在ProfileEditor
中将this
输出为onChange
。
这里是Babel输出中的SyncedTextField
构造函数:
function SyncedTextField() {
var _ref2;
var _temp2, _this2, _ret2;
_classCallCheck(this, SyncedTextField);
for (
var _len2 = arguments.length, args = Array(_len2), _key2 = 0;
_key2 < _len2;
_key2++
) {
args[_key2] = arguments[_key2];
}
return (
(_ret2 = ((_temp2 = ((_this2 = _possibleConstructorReturn(
this,
(_ref2 =
SyncedTextField.__proto__ ||
Object.getPrototypeOf(SyncedTextField)).call.apply(
_ref2,
[this].concat(args)
)
)),
_this2)),
(_this2.onChange = function(event) {
console.log(_this); // SHOULD BE _this2
}),
_temp2)),
_possibleConstructorReturn(_this2, _ret2)
);
}
请注意,编译器会创建_this
,_this2
等临时变量以在箭头函数中提供词法this
,但是Babel使用了错误的变量。
onChange = ...
类字段是用于以下方面的语法糖:
constructor(...args) {
super(...args);
this.onChange = event => {
console.log(this);
};
}
当the example is changed from class fields to constructor code时,它输出SyncedTextField
。
The same example使用TypeScript编译(在React模板中默认由Stackblitz使用),可以按预期工作,并在SyncedTextField
中将this
输出为onChange
。
由于很少用这种方式定义类,因此Babel错误通常不适用。
SyncedTextField = class SyncedTextField extends Component {...}
是反模式。没有理由像这样嵌套类表达式。它的效率很低,因为它是在每个ProfileEditor
实例上求值的。它应该是类声明,可以用作<SyncedTextField/> this way.
即使出于可测试性或可扩展性的原因,即使应将SyncedTextField
定义为ProfileEditor
组件的属性,也最好将其设置为原型属性:
class SyncedTextField extends Component {...}
class ProfileEditor extends Component {
get SyncedTextField() { return SyncedTextField }
...
}