javascript React.js ES6 避免将“this”绑定到每个方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32192682/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
React.js ES6 avoid binding 'this' to every method
提问by Pavlin
Recently, I've started tinkering with React.js and I love it. I started out in the regular ES5, so as to get the hang of things, the docs are all written in ES5...
最近,我开始修改 React.js 并且我喜欢它。我从普通的 ES5 开始,为了掌握事情的窍门,文档都是用 ES5 编写的......
But now I wanted to try ES6, because it's shiny and new, and it does seem to simplify some things. What bothers me a lot is that for every method I had added into my component classes I now have to bind 'this' to, otherwise it doesn't work. So my constructor ends up looking like this:
但是现在我想尝试 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);
}
If I were to add even more methods to my class, this would become an even bigger, uglier mess.
如果我要在我的类中添加更多的方法,这将成为一个更大、更丑陋的混乱。
My question is, is there some way to get around this, or at least make it easier, shorter and less ugly? One of the main reasons I wanted to try React with ES6 was to make my code more concise, but this is doing the opposite. Any suggestions or input would be appreciated.
我的问题是,有没有办法解决这个问题,或者至少让它更容易、更短、更不丑?我想用 ES6 尝试 React 的主要原因之一是让我的代码更简洁,但这恰恰相反。任何建议或意见将不胜感激。
采纳答案by Ross Allen
You can use class fieldsto do the binding outside the constructor. They look like the following:
您可以使用类字段在构造函数之外进行绑定。它们如下所示:
class Foo extends React.Component {
handleBar = () => {
console.log('neat');
};
handleFoo = () => {
console.log('cool');
};
render() {
return (
<div
onClick={this.handleBar}
onMouseOver={this.handleFoo}
/>
);
}
}
Class fields are supported experimentally by Babel via its class properties transform, but they are still "experimental" because they are a Stage 3 Draft(not yet in a Babel preset).
Babel 通过其类属性 transform实验性地支持类字段,但它们仍然是“实验性的”,因为它们是第 3 阶段草案(尚未在 Babel 预设中)。
You will need to do the binding manually until ES7 or until enabling the feature in Babel, however. This topic is covered briefly in Babel's blog post on React on ES6+.
但是,在 ES7 或在 Babel 中启用该功能之前,您需要手动进行绑定。Babel 关于React on ES6+的博客文章中简要介绍了这个主题。
回答by Brigand
Another alternative is to use decorators. You declare a getter on the prototype, and on first access for an instance it defines an own property with a bound version of that function.
另一种选择是使用装饰器。您在原型上声明了一个 getter,并且在第一次访问实例时,它定义了一个自己的属性,该属性具有该函数的绑定版本。
But there's a catch! In development it won't replace the property, it'll bind on every access. This means you don't break react-hot-loader. At least for me, that's pretty important.
但是有一个问题!在开发中,它不会替换属性,它会在每次访问时绑定。这意味着您不会破坏 react-hot-loader。至少对我来说,这很重要。
I created a library, class-bind, that provides this.
我创建了一个库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>
);
}
}
Decorators too unstable for you? You can bind everything or some things with the same benefits.
装饰器对你来说太不稳定了?您可以绑定具有相同好处的所有内容或某些内容。
import {bind, bindAll} from 'class-bind';
bind(App.prototype, 'returnsFoo');
// or
bindAll(App.prototype);
回答by Jon Jaques
If you use stage-0
there is a function binding syntax.
如果您使用stage-0
,则有一个函数绑定语法。
class MyComp extends Component {
handleClick() { console.log('doing things') }
render() {
return <button onClick={::this.handleClick}>Do Things</button>
}
}
This destructures to this.handleClick.call(this)
, which I think is generally performant enough.
这会分解为this.handleClick.call(this)
,我认为它通常具有足够的性能。
回答by Alexander Mills
One idea to avoid bind
一种避免绑定的想法
class MyComp extends Component {
render() {
return <button onClick={e => this.handleClick(e)}>Do Things</button>
}
}
disclaimer: untested, also, cannot easily handle more than one argument (in this case, there is one, event (e).
免责声明:未经测试,也不能轻松处理多个参数(在这种情况下,有一个,事件(e)。
Also, this is answer is probably an example of what notto do, according to this article which is probably a worthwhile read:
此外,根据这篇可能值得一读的文章,这个答案可能是不该做什么的一个例子:
回答by Jon Pellant
I actually prefer to imitate OOP inheritance by passing children the parent context.
我实际上更喜欢通过将父上下文传递给孩子来模仿 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, Jon
0.02 美元,乔恩
回答by Pablo Darde
I created a method to organize all the "binds".
我创建了一种组织所有“绑定”的方法。
class MyClass {
constructor() {
this.bindMethods([
'updateLocationFields',
'render',
'loadCities',
]);
}
bindMethods(methods) {
methods.forEach((item) => {
this[item] = this[item].bind(this);
});
}
...
}
回答by J. Mark Stevens
Ssorallen's suggestion is great but if you want another way there is:
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()); }
}
You can add any number of methods to this.binder('method1', 'method2', ...)
您可以向 this.binder('method1', 'method2', ...) 添加任意数量的方法
回答by James
I use a helper function doBinding(this)
, which I call in each constructor. In this example it binds _handleChange1()
and _handleChange2()
.
我使用了一个辅助函数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() {
...
}
}
The method works even if you are not using Babel.
即使您没有使用 Babel,该方法也有效。
My handler methods all begin with _
(a convention to indicate they are private). So doBinding()
looks for the _
. You can remove the if (key.startsWith("_"))
if you don't use this convention.
我的处理程序方法都以_
(表示它们是私有的约定)开头。所以doBinding()
寻找_
. 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);
}
}
}
回答by MaxP
How about using a common function to do the binding-work like this:
如何使用一个通用函数来做这样的绑定工作:
// 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){
//...
}
})
}
}