[英]React.js ES6 avoid binding 'this' to every method
最近,我开始修改 React.js 并且我喜欢它。 我从普通的 ES5 开始,为了掌握窍门,文档都是用 ES5 编写的......
但是现在我想尝试 ES6,因为它闪亮且新颖,而且似乎确实简化了一些事情。 令我困扰的是,对于我添加到组件类中的每个方法,我现在必须将“this”绑定到,否则它不起作用。 所以我的构造函数最终看起来像这样:
constructor(props) {
super(props);
this.state = { ...some initial state... }
this.someHandler = this.someHandler.bind(this);
this.someHandler = this.someHandler.bind(this);
this.someHandler = this.someHandler.bind(this);
this.someHandler = this.someHandler.bind(this);
this.someHandler = this.someHandler.bind(this);
this.someHandler = this.someHandler.bind(this);
this.someHandler = this.someHandler.bind(this);
}
如果我要在我的类中添加更多的方法,这将成为一个更大、更丑陋的混乱。
我的问题是,有没有办法解决这个问题,或者至少让它更容易、更短、更不丑? 我想用 ES6 尝试 React 的主要原因之一是让我的代码更简洁,但这恰恰相反。 任何建议或意见将不胜感激。
您可以使用类字段在构造函数之外进行绑定。 它们如下所示:
class Foo extends React.Component {
handleBar = () => {
console.log('neat');
};
handleFoo = () => {
console.log('cool');
};
render() {
return (
<div
onClick={this.handleBar}
onMouseOver={this.handleFoo}
/>
);
}
}
Babel 通过其类属性 transform实验性地支持类字段,但它们仍然是“实验性的”,因为它们是第 3 阶段草案(尚未在 Babel 预设中)。
但是,在 ES7 或在 Babel 中启用该功能之前,您需要手动进行绑定。 Babel 关于React on ES6+的博客文章中简要介绍了这个主题。
另一种选择是使用装饰器。 您在原型上声明了一个 getter,并且在第一次访问实例时,它定义了一个自己的属性,该属性具有该函数的绑定版本。
但是有一个问题! 在开发中,它不会替换属性,它会在每次访问时绑定。 这意味着您不会破坏 react-hot-loader 。 至少对我来说,这很重要。
我创建了一个库, class-bind ,它提供了这个。
import {bound} from 'class-bind';
class App {
constructor(){
this.foo = 'bar';
}
@bound
returnsFoo(){
return this.foo;
}
render(){
var returnsFoo = this.returnsFoo;
return (
<div>
{returnsFoo()} === 'bar'
</div>
);
}
}
装饰器对你来说太不稳定了? 您可以绑定具有相同好处的所有内容或某些内容。
import {bind, bindAll} from 'class-bind';
bind(App.prototype, 'returnsFoo');
// or
bindAll(App.prototype);
Ssorallen 的建议很好,但如果你想要另一种方式,有:
class AppCtrlRender extends Component {
binder(...methods) { methods.forEach( (method) => this[method] = this[method].bind(this) ); }
render() {
var isMobile = this.state.appData.isMobile;
var messages = this.state.appData.messages;
return (
<div id='AppCtrlSty' style={AppCtrlSty}>
React 1.3 Slider
<br/><br/>
<div className='FlexBoxWrap'>
<Slider isMobile={isMobile}/>
<JList data={messages}/>
</div>
</div>
);
}
}
var getAppState = function() {
return {
appData: AppStore.getAppData()
};
};
export default class AppCtrl extends AppCtrlRender {
constructor() {
super();
this.state = getAppState();
this.binder('appStoreDidChange');
}
componentDidMount() {
var navPlatform = window.navigator.platform;
Actions.setWindowDefaults(navPlatform);
}
componentWillMount() { AppStore.onAny(this.appStoreDidChange); }
componentWillUnmount() { AppStore.offAny(this.appStoreDidChange); }
appStoreDidChange() { this.setState(getAppState()); }
}
您可以向 this.binder('method1', 'method2', ...) 添加任意数量的方法
如果您使用stage-0
,则有一个函数绑定语法。
class MyComp extends Component {
handleClick() { console.log('doing things') }
render() {
return <button onClick={::this.handleClick}>Do Things</button>
}
}
这会this.handleClick.call(this)
为this.handleClick.call(this)
,我认为它通常具有足够的性能。
一种避免绑定的想法
class MyComp extends Component {
render() {
return <button onClick={e => this.handleClick(e)}>Do Things</button>
}
}
免责声明:未经测试,也不能轻松处理多个参数(在这种情况下,有一个,事件(e)。
此外,根据这篇可能值得一读的文章,这个答案可能是不该做什么的一个例子:
我实际上更喜欢通过将父上下文传递给孩子来模仿 OOP 继承。
class Parent extends Component {
state = {happy: false}
changeState(happy) {
this.setState({happy})
}
render() {
return (
<Child parent={this} >
)
}
}
class Child extends Component {
//...
this.props.parent.changeState(true)
}
0.02 美元,乔恩
我创建了一种组织所有“绑定”的方法。
class MyClass {
constructor() {
this.bindMethods([
'updateLocationFields',
'render',
'loadCities',
]);
}
bindMethods(methods) {
methods.forEach((item) => {
this[item] = this[item].bind(this);
});
}
...
}
我使用了一个辅助函数doBinding(this)
,我在每个构造函数中调用它。 在这个例子中,它绑定了_handleChange1()
和_handleChange2()
。
class NameForm extends React.Component {
constructor(props) {
super(props);
doBinding(this);
this.state = {value1: "", value2: ""};
}
_handleChange1(event) {
this.setState({value1: event.target.value});
}
_handleChange2(event) {
this.setState({value2: event.target.value});
}
render() {
...
}
}
即使您不使用 Babel,该方法也有效。
我的处理程序方法都以_
开头(表示它们是私有的约定)。 所以doBinding()
寻找_
。 如果不使用此约定, if (key.startsWith("_"))
可以删除if (key.startsWith("_"))
。
function doBinding(obj) {
const proto = Object.getPrototypeOf(obj);
for (const key of Object.getOwnPropertyNames(proto)) {
if (key.startsWith("_")) {
obj[key] = obj[key].bind(obj);
}
}
}
如何使用一个通用函数来做这样的绑定工作:
// common function:
function bind(self,methods){
for(var key in methods){
self[key] = methods[key].bind(self);
}
}
// your class:
class MyClass {
constructor() {
bind(this,{
someHandler1(event){
//...
},
someHandler2(event){
//...
}
})
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.