javascript reactjs ecmascript-6 es2015

javascript - React.js ES6 evita vincular ''esto'' a cada método



reactjs ecmascript-6 (8)

Creé un método para organizar todos los "enlaces".

class MyClass { constructor() { this.bindMethods([ ''updateLocationFields'', ''render'', ''loadCities'', ]); } bindMethods(methods) { methods.forEach((item) => { this[item] = this[item].bind(this); }); } ... }

Recientemente, comencé a jugar con React.js y me encanta. Comencé en el ES5 regular, para poder entender las cosas, todos los documentos están escritos en ES5 ...

Pero ahora quería probar ES6, porque es brillante y nuevo, y parece simplificar algunas cosas. Lo que me molesta mucho es que por cada método que he agregado a mis clases de componentes ahora tengo que vincular ''esto'', de lo contrario no funciona. Entonces mi constructor termina luciendo así:

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 tuviera que agregar aún más métodos a mi clase, esto se convertiría en un desastre aún más grande y feo.

Mi pregunta es, ¿hay alguna forma de evitar esto, o al menos hacerlo más fácil, más corto y menos feo? Una de las principales razones por las que quería probar React with ES6 era hacer que mi código fuera más conciso, pero esto está haciendo lo contrario. Cualquier sugerencia o aportación sería apreciada.


De hecho, prefiero imitar la herencia OOP pasando a los niños el contexto principal.

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


La sugerencia de Ssorallen es genial, pero si quieres otra forma hay:

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

Puede agregar cualquier cantidad de métodos a this.binder (''método1'', ''método2'', ...)


Otra alternativa es usar decoradores. Usted declara un captador en el prototipo, y en el primer acceso para una instancia, define una propiedad propia con una versión enlazada de esa función.

Pero hay una trampa! En desarrollo, no reemplazará la propiedad, se vinculará en cada acceso. Esto significa que no interrumpe reaccionar-hot-loader . Al menos para mí, eso es bastante importante.

Creé una biblioteca, class-bind , que proporciona esto.

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

¿Decoradores demasiado inestables para ti? Puede vincular todo o algunas cosas con los mismos beneficios.

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


Puede usar campos de clase para hacer el enlace fuera del constructor. Se parecen a lo siguiente:

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

Babel admite experimentalmente los campos de clase a través de su transformación de propiedades de clase , pero aún son "experimentales" porque son un borrador de la Etapa 3 (aún no están en un preset de Babel).

Sin embargo, deberá realizar el enlace manualmente hasta ES7 o hasta habilitar la función en Babel. Este tema se trata brevemente en la publicación del blog de Babel sobre React en ES6 + .


Si usa stage-0 hay una sintaxis de enlace de función.

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

Esto destruye this.handleClick.call(this) , que creo que generalmente es lo suficientemente this.handleClick.call(this) .


Una idea para evitar atar

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

descargo de responsabilidad: no probado, tampoco puede manejar fácilmente más de un argumento (en este caso, hay uno, evento (e).

Además, esta respuesta es probablemente un ejemplo de lo que no se debe hacer, de acuerdo con este artículo, que probablemente sea una lectura que valga la pena:

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


Utilizo una función auxiliar doBinding(this) , que llamo en cada constructor. En este ejemplo, une _handleChange1() y _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() { ... } }

El método funciona incluso si no está utilizando Babel.

Todos mis métodos de controlador comienzan con _ (una convención para indicar que son privados). Entonces doBinding() busca el _ . Puede eliminar el if (key.startsWith("_")) si no utiliza esta convención.

function doBinding(obj) { const proto = Object.getPrototypeOf(obj); for (const key of Object.getOwnPropertyNames(proto)) { if (key.startsWith("_")) { obj[key] = obj[key].bind(obj); } } }