React.js ES6 évite de lier 'this' à chaque méthode

Récemment, j'ai commencé à bricoler avec React.js et je l'aime. J'ai commencé dans l'ES5 régulier, afin d'obtenir le coup de choses, les docs sont tous écrits dans ES5 …

Mais maintenant, je voulais essayer ES6, car il est brillant et neuf, et cela semble simplifier certaines choses. Ce qui me dérange beaucoup, c'est que, pour chaque méthode que j'ai ajoutée dans mes classes de composants, je dois maintenant lier «cela», sinon cela ne fonctionne pas. Donc, mon constructeur finit par ressembler à ceci:

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); } 

Si j'ajoute encore plus de méthodes à ma classe, cela deviendrait un désordre encore plus gros et plus laid.

Ma question est, est-ce qu'il y a un moyen de contourner cela, ou au moins le rendre plus facile, plus court et moins laid? L'une des principales raisons pour lesquelles je voulais essayer Réagir avec ES6 était de rendre mon code plus concis, mais cela fait le contraire. Toute suggestion ou contribution serait appréciée.

Il existe une proposition ES7 pour les initiateurs de propriétés de classe qui évitera la nécessité de faire la liaison dans le constructeur en permettant aux fonctions de flèche de se lier à l'instance. Ils ressemblent à ce qui suit:

 class Foo extends React.Component { handleBar = () => { console.log('neat'); }; handleFoo = () => { console.log('cool'); }; render() { return ( <div onClick={this.handleBar} onMouseOver={this.handleFoo} /> ); } } 

Les initiateurs de propriétés de classe sont supportés expérimentalement par Babel via ses transformations de propriétés de classe , mais ils sont encore "expérimentaux" car ils sont une proposition de Stage 2 (pas encore dans un preset Babel).

Vous devrez faire la liaison manuellement jusqu'à ES7 ou en autorisant la fonctionnalité dans Babel, cependant. Ce sujet est brièvement abordé dans le blog de Babel sur React sur ES6 + .

Une autre alternative est d'utiliser des décorateurs. Vous déclarez un getter sur le prototype et, lors du premier accès pour une instance, il définit une propriété propre avec une version liée de cette fonction.

Mais il y a une prise! En cours de développement, il ne remplacera pas la propriété, elle se liera à chaque accès. Cela signifie que vous ne brisez pas le réchauffeur-chargeur . Au moins pour moi, c'est très important.

J'ai créé une bibliothèque, classe-bind , qui fournit ceci.

 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> ); } } 

Des décorateurs trop instables pour vous? Vous pouvez lier tout ou certaines choses avec les mêmes avantages.

 import {bind, bindAll} from 'class-bind'; bind(App.prototype, 'returnsFoo'); // or bindAll(App.prototype); 

La suggestion de Ssorallen est géniale, mais si vous voulez une autre façon, il y a:

  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()); } } 

Vous pouvez ajouter n'importe quel nombre de méthodes à this.binder ('method1', 'method2', …)

Si vous utilisez l' stage-0 il existe une syntaxe de liaison de fonction.

 class MyComp extends Component { handleClick() { console.log('doing things') } render() { return <button onClick={::this.handleClick}>Do Things</button> } } 

Cette this.handleClick.call(this) de this.handleClick.call(this) , qui, selon moi, est généralement assez performante.

Une idée à éviter de lier

 class MyComp extends Component { render() { return <button onClick={e => this.handleClick(e)}>Do Things</button> } } 

Avertissement: non testé, ne peut pas facilement gérer plus d'un argument (dans ce cas, il y en a un, événement (e).

En outre, cette réponse est probablement un exemple de ce qu'il ne faut pas faire, selon cet article qui est probablement une lecture valable:

https://daveceddia.com/avoid-bind-when-passing-props/