vida son recibe react que props parámetros método los funciones eventos español entre cuáles conceptos comunicacion componentes ciclo javascript reactjs ecmascript-6 babeljs

javascript - son - No se puede acceder a la instancia de React(this) dentro del controlador de eventos



react comunicacion entre componentes (18)

Alexandre Kirszenberg tiene razón, pero otra cosa importante a la que debe prestar atención es dónde coloca su encuadernación. He estado atrapado en una situación durante días (probablemente porque soy un principiante), pero a diferencia de otros, sabía sobre la vinculación (que ya había aplicado), por lo que no podía entender por qué todavía tenía esos errores Resulta que tuve el enlace en el orden incorrecto.

Otro es quizás el hecho de que estaba llamando a la función dentro de "this.state", que no estaba al tanto del enlace porque estaba por encima de la línea de enlace,

A continuación se muestra lo que tenía (Por cierto, esta es mi primera publicación, pero pensé que era muy importante, ya que no pude encontrar la solución en ningún otro lado):

constructor(props){ super(props); productArray=//some array this.state={ // Create an Array which will hold components to be displayed proListing:productArray.map(product=>{return(<ProRow dele={this.this.popRow()} prodName={product.name} prodPrice={product.price}/>)}) } this.popRow=this.popRow.bind(this);//This was the Issue, This line //should be kept above "this.state"

Estoy escribiendo un componente simple en ES6 (con BabelJS) y funciona this.setState no funciona.

Los errores típicos incluyen algo como

No se puede leer la propiedad ''setState'' de undefined

o

this.setState no es una función

¿Sabes por qué? Aquí está el código:

import React from ''react'' class SomeClass extends React.Component { constructor(props) { super(props) this.state = {inputContent: ''startValue''} } sendContent(e) { console.log(''sending input content ''+React.findDOMNode(React.refs.someref).value) } changeContent(e) { this.setState({inputContent: e.target.value}) } render() { return ( <div> <h4>The input form is here:</h4> Title: <input type="text" ref="someref" value={this.inputContent} onChange={this.changeContent} /> <button onClick={this.sendContent}>Submit</button> </div> ) } } export default SomeClass


Aunque las respuestas anteriores han proporcionado una descripción general básica de las soluciones (es decir, enlace, funciones de flecha, decoradores que hacen esto por usted), todavía no he encontrado una respuesta que realmente explique por qué esto es necesario, que en mi opinión es la raíz de confusión, y conduce a pasos innecesarios, como un reencuentro innecesario y seguir ciegamente lo que otros hacen.

this es dinámico

Para comprender esta situación específica, una breve introducción de cómo funciona. La clave aquí es que this es un enlace de tiempo de ejecución y depende del contexto de ejecución actual. Por lo tanto, por qué se conoce comúnmente como "contexto", es decir, dar información sobre el contexto de ejecución actual, y por qué necesita vincular es porque pierde "contexto". Pero déjenme ilustrar el problema con un fragmento:

const foobar = { bar: function () { return this.foo; }, foo: 3, }; console.log(foobar.bar()); // 3, all is good!

En este ejemplo, obtenemos 3 , como se esperaba. Pero tome este ejemplo:

const barFunc = foobar.bar; console.log(barFunc()); // Uh oh, undefined!

Puede ser inesperado descubrir que registra registros indefinidos, ¿a dónde fueron los 3 ? La respuesta se encuentra en el "contexto" , o cómo ejecuta una función. Compare cómo llamamos a las funciones:

// Example 1 foobar.bar(); // Example 2 const barFunc = foobar.bar; barFunc();

Note la diferencia En el primer ejemplo, estamos especificando exactamente dónde se encuentra el método de bar 1 , en el objeto foobar :

foobar.bar(); ^^^^^^

Pero en el segundo, almacenamos el método en una nueva variable, y usamos esa variable para llamar al método, sin indicar explícitamente dónde existe realmente el método, perdiendo así el contexto :

barFunc(); // Which object is this function coming from?

Y ahí radica el problema, cuando almacena un método en una variable, se pierde la información original sobre dónde se encuentra ese método (el contexto en el que se está ejecutando el método). Sin esta información, en tiempo de ejecución, no hay forma de que el intérprete de JavaScript vincule this correctamente; sin un contexto específico, this no funciona como se esperaba 2 .

Relativo a reaccionar

Aquí hay un ejemplo de un componente React (acortado por brevedad) que sufre this problema:

handleClick() { this.setState(({ clicks }) => ({ // setState is async, use callback to access previous state clicks: clicks + 1, // increase by 1 })); } render() { return ( <button onClick={this.handleClick}>{this.state.clicks}</button> ); }

Pero, ¿por qué y cómo se relaciona la sección anterior con esto? Esto se debe a que sufren de una abstracción del mismo problema. Si observa cómo React maneja los controladores de eventos :

// Edited to fit answer, React performs other checks internally // props is the current React component''s props, registrationName is the name of the event handle prop, i.e "onClick" let listener = props[registrationName]; // Later, listener is called

Entonces, cuando haces onClick={this.handleClick} , el método this.handleClick finalmente se asigna a la listener variable 3 . Pero ahora puede ver surgir el problema: dado que hemos asignado this.handleClick al listener , ¡ya no especificamos exactamente de dónde handleClick ! Desde el punto de vista de React, el listener es solo una función, no asociada a ningún objeto (o en este caso, instancia del componente React). Hemos perdido contexto y, por lo tanto, el intérprete no puede inferir this valor para usar dentro de handleClick .

Por qué funciona la encuadernación

Quizás se pregunte, si el intérprete decide this valor en tiempo de ejecución, ¿por qué puedo vincular el controlador para que funcione ? Esto se debe a que puede usar la Function#bind para garantizar this valor en tiempo de ejecución. Esto se hace estableciendo una propiedad interna de enlace en una función, lo que le permite no inferir this :

this.handleClick = this.handleClick.bind(this);

Cuando se ejecuta esta línea, presumiblemente en el constructor, la corriente se captura (la instancia del componente React) y se establece como un enlace interno de una función completamente nueva, devuelta desde la Function#bind . Esto asegura que cuando this se calcule en tiempo de ejecución, el intérprete no tratará de inferir nada, sino que usará el valor que le proporcionó.

Por qué funcionan las propiedades de la función de flecha

Las propiedades de clase de función de flecha actualmente funcionan a través de Babel en función de la transpilación:

handleClick = () => { /* Can use this just fine here */ }

Se convierte en:

constructor() { super(); this.handleClick = () => {} }

Y esto funciona debido al hecho de que las funciones de flecha no se unen a esto, sino que toman this de su alcance envolvente. En este caso, el constructor es this , que apunta a la instancia del componente React, lo que le da la respuesta correcta. 4 4

1 Uso "método" para referirme a una función que se supone que está vinculada a un objeto, y "función" para aquellos que no lo están.

2 En el segundo fragmento, undefined se registra en lugar de 3 porque el valor predeterminado es el contexto de ejecución global ( window cuando no está en modo estricto o no está undefined ) cuando no se puede determinar a través de un contexto específico. Y en el ejemplo window.foo no existe, por lo tanto, no se window.foo .

3 Si va por la madriguera del conejo de cómo se ejecutan los eventos en la cola de eventos, se invokeGuardedCallback en el oyente.

4 En realidad es mucho más complicado . React trata internamente de usar Function#apply en los oyentes para su propio uso, pero esto no funciona con las funciones de flecha, ya que simplemente no vinculan this . Eso significa que cuando this se evalúa dentro de la función de flecha, se resuelve en cada entorno léxico de cada contexto de ejecución del código actual del módulo. El contexto de ejecución que finalmente resuelve tener un enlace this es el constructor, que apunta a la instancia actual del componente React, lo que le permite funcionar.


En caso de que desee mantener el enlace en la sintaxis del constructor, puede usar el proposal-bind-operator y transformar su código de la siguiente manera:

constructor() { this.changeContent = ::this.changeContent; }

En lugar de :

constructor() { this.changeContent = this.changeContent.bind(this); }

mucho más simple, sin necesidad de bind(this) o fatArrow .


Está utilizando ES6, por lo que las funciones no se unirán a "este" contexto automáticamente. Debe vincular manualmente la función al contexto.

constructor(props) { super(props); this.changeContent = this.changeContent.bind(this); }


Este problema es una de las primeras cosas que la mayoría de nosotros experimentamos, cuando React.createClass() la transición de la sintaxis de definición del componente React.createClass() a la forma de la clase ES6 de extender React.Component .

Es causado por las diferencias de this contexto en React.createClass() vs extends React.Component .

El uso de React.createClass() automáticamente this contexto (valores) correctamente, pero ese no es el caso cuando se usan clases ES6. Al hacerlo de la manera ES6 (al extender React.Component ), this contexto es null por defecto. Las propiedades de la clase no se unen automáticamente a la instancia de la clase React (componente).

Enfoques para resolver este problema

Conozco un total de 4 enfoques generales.

  1. Vincula tus funciones en el constructor de la clase . Considerado por muchos como un enfoque de mejores prácticas que evita tocar JSX y no crea una nueva función en cada renderizado de componentes.

    class SomeClass extends React.Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); } handleClick() { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick}></button> ); } }

  2. Vincula tus funciones en línea . Todavía puede encontrar este enfoque utilizado aquí y allá en algunos tutoriales / artículos / etc., por lo que es importante que lo sepa. Es el mismo concepto que el # 1, pero tenga en cuenta que vincular una función crea una nueva función por cada representación.

    class SomeClass extends React.Component { handleClick() { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick.bind(this)}></button> ); } }

  3. Utiliza una función de flecha gruesa . Hasta que las funciones de flecha, cada nueva función definió su propio valor. Sin embargo, la función de flecha no crea su propio contexto, por this tiene el significado original de la instancia del componente React. Por lo tanto, podemos:

    class SomeClass extends React.Component { handleClick() { console.log(this); // the React Component instance } render() { return ( <button onClick={ () => this.handleClick() }></button> ); } }

    o

    class SomeClass extends React.Component { handleClick = () => { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick}></button> ); } }

  4. Use la biblioteca de funciones de utilidad para vincular automáticamente sus funciones . Existen algunas bibliotecas de utilidades, que automáticamente hacen el trabajo por usted. Estos son algunos de los populares, solo por mencionar algunos:

    • Autobind Decorator es un paquete NPM que une los métodos de una clase a la instancia correcta de this , incluso cuando los métodos están separados. El paquete utiliza @autobind antes de los métodos para vincular this a la referencia correcta al contexto del componente.

      import autobind from ''autobind-decorator''; class SomeClass extends React.Component { @autobind handleClick() { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick}></button> ); } }

      Autobind Decorator es lo suficientemente inteligente como para permitirnos vincular todos los métodos dentro de una clase de componentes a la vez, al igual que el enfoque n. ° 1.

    • Class Autobind es otro paquete de NPM que se usa ampliamente para resolver este problema de enlace. A diferencia de Autobind Decorator, no utiliza el patrón de decorador, sino que realmente usa una función dentro de su constructor que vincula automáticamente los métodos del Componente a la referencia correcta de this .

      import autobind from ''class-autobind''; class SomeClass extends React.Component { constructor() { autobind(this); // or if you want to bind only only select functions: // autobind(this, ''handleClick''); } handleClick() { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick}></button> ); } }

      PD: Otra biblioteca muy similar es React Autobind .

Recomendación

Si yo fuera usted, me quedaría con el enfoque # 1. Sin embargo, tan pronto como obtenga un montón de enlaces en el constructor de su clase, le recomendaría que explore una de las bibliotecas auxiliares mencionadas en el enfoque n. ° 4.

Otro

No está relacionado con el problema que tiene, pero no debe usar en exceso las referencias .

Su primera inclinación puede ser usar referencias para "hacer que las cosas sucedan" en su aplicación. Si este es el caso, tómese un momento y piense más críticamente sobre dónde debe pertenecer el estado en la jerarquía de componentes.

Para fines similares, al igual que el que necesita, el uso de un componente controlado es la forma preferida. Le sugiero que considere usar su state Componente . Entonces, simplemente puede acceder al valor de esta manera: this.state.inputContent .


Este problema ocurre después de react15.0, que el controlador de eventos no se vinculó automáticamente al componente. por lo que debe vincular esto al componente manualmente siempre que se llame al controlador de eventos.

Existen varios métodos para resolver el problema. pero necesita saber qué método es mejor y por qué? En general, recomendamos vincular sus funciones en el constructor de la clase o usar una función de flecha.

// method 1: use a arrow function class ComponentA extends React.Component { eventHandler = () => { console.log(this) } render() { return ( <ChildComponent onClick={this.eventHandler} /> ); } // method 2: Bind your functions in the class constructor. class ComponentA extends React.Component { constructor(props) { super(props); this.eventHandler = this.eventHandler.bind(this); } render() { return ( <ChildComponent onClick={this.eventHandler} /> ); }

estos dos métodos no crearán una nueva función cuando el componente se procese cada vez. por lo tanto, nuestro ChildComponent no se volverá a representar debido al cambio de los accesorios de la nueva función, o puede producir el problema de rendimiento.


Este problema ocurre porque this.changeContent y onClick={this.sendContent} no están vinculados a esto de la instancia del componente.

Hay otra solución (además de usar bind () en el constructor ()) para usar las funciones de flecha de ES6 que comparten el mismo alcance léxico del código circundante y mantener esto , para que pueda cambiar su código en render () a ser:

render() { return ( <input type="text" onChange={ () => this.changeContent() } /> <button onClick={ () => this.sendContent() }>Submit</button> ) }


Hola, si no quieres preocuparte por vincular tu llamada de función. Puede usar ''class-autobind'' e importarlo así

import autobind from ''class-autobind''; class test extends Component { constructor(props){ super(props); autobind(this); }

No escriba autobind antes de la súper llamada porque no funcionará


Mi recomendación es usar las funciones de flecha como propiedades

class SomeClass extends React.Component { handleClick = () => { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick}></button> ); } }

y no use las funciones de flecha como

class SomeClass extends React.Component { handleClick(){ console.log(this); // the React Component instance } render() { return ( <button onClick={()=>{this.handleClick}}></button> ); } }

porque el segundo enfoque generará una nueva función en cada llamada de renderizado, de hecho, esto significa una nueva versión de puntero nueva, que si luego se preocupa por el rendimiento, puede usar React.PureComponent o en React.Component puede anular shouldComponentUpdate (nextProps, nextState) y control superficial cuando llegaron los accesorios


Morhaus es correcto, pero esto se puede resolver sin bind .

Puede usar una función de flecha junto con la propuesta de propiedades de clase :

class SomeClass extends React.Component { changeContent = (e) => { this.setState({inputContent: e.target.value}) } render() { return <input type="text" onChange={this.changeContent} />; } }

Debido a que la función de flecha se declara en el alcance del constructor, y porque las funciones de flecha mantienen this desde su alcance de declaración, todo funciona. La desventaja aquí es que estas no serán funciones en el prototipo, todas serán recreadas con cada componente. Sin embargo, esto no es un gran inconveniente, ya que la bind da como resultado lo mismo.


Necesitamos vincular la función de evento con el componente en el constructor de la siguiente manera,

import React from ''react'' class SomeClass extends React.Component { constructor(props) { super(props) this.state = {inputContent: ''startValue''} this.changeContent = this.changeContent.bind(this); } sendContent(e) { console.log(''sending input content ''+React.findDOMNode(React.refs.someref).value) } changeContent(e) { this.setState({inputContent: e.target.value}) } render() { return ( <div> <h4>The input form is here:</h4> Title: <input type="text" ref="someref" value={this.inputContent} onChange={this.changeContent} /> <button onClick={this.sendContent}>Submit</button> </div> ) } } export default SomeClass

Gracias


Puedes abordar esto de tres maneras

1. Vincula la función de evento en el propio constructor de la siguiente manera

import React from ''react'' class SomeClass extends React.Component { constructor(props) { super(props) this.state = {inputContent: ''startValue''} this.changeContent = this.changeContent.bind(this); } sendContent(e) { console.log(''sending input content ''+React.findDOMNode(React.refs.someref).value) } changeContent(e) { this.setState({inputContent: e.target.value}) } render() { return ( <div> <h4>The input form is here:</h4> Title: <input type="text" ref="someref" value={this.inputContent} onChange={this.changeContent} /> <button onClick={this.sendContent}>Submit</button> </div> ) } } export default SomeClass

2.Enlazar cuando se llama

import React from ''react'' class SomeClass extends React.Component { constructor(props) { super(props) this.state = {inputContent: ''startValue''} } sendContent(e) { console.log(''sending input content ''+React.findDOMNode(React.refs.someref).value) } changeContent(e) { this.setState({inputContent: e.target.value}) } render() { return ( <div> <h4>The input form is here:</h4> Title: <input type="text" ref="someref" value={this.inputContent} onChange={this.changeContent} /> <button onClick={this.sendContent.bind(this)}>Submit</button> </div> ) } } export default SomeClass

3. Mediante el uso de funciones de flecha

import React from ''react'' class SomeClass extends React.Component { constructor(props) { super(props) this.state = {inputContent: ''startValue''} } sendContent(e) { console.log(''sending input content ''+React.findDOMNode(React.refs.someref).value) } changeContent(e) { this.setState({inputContent: e.target.value}) } render() { return ( <div> <h4>The input form is here:</h4> Title: <input type="text" ref="someref" value={this.inputContent} onChange={this.changeContent} /> <button onClick={()=>this.sendContent()}>Submit</button> </div> ) } } export default SomeClass


Puedes resolver esto siguiendo estos pasos

Cambie la función sendContent con

sendContent(e) { console.log(''sending input content ''+this.refs.someref.value) }

Cambiar la función de renderizado con

<input type="text" ref="someref" value={this.state.inputContent} onChange={(event)=>this.changeContent(event)} /> <button onClick={(event)=>this.sendContent(event)}>Submit</button>


Solución:

  1. Sin un enlace explícito, bind con el nombre del método, puedes usar la sintaxis de funciones de flecha gruesa () => {} que mantiene el contexto de this .

import React from ''react'' class SomeClass extends React.Component { constructor(props) { super(props) this.state = { inputContent: ''startValue'' } } sendContent = (e) => { console.log(''sending input content '',this.state.inputContent); } changeContent = (e) => { this.setState({inputContent: e.target.value},()=>{ console.log(''STATE:'',this.state); }) } render() { return ( <div> <h4>The input form is here:</h4> Title: <input type="text" value={this.state.inputContent} onChange={this.changeContent} /> <button onClick={this.sendContent}>Submit</button> </div> ) } } export default SomeClass

Otras soluciones:

  1. Vincula tus funciones en el constructor de la clase.

  2. Vincula tus funciones en la plantilla JSX que se escapa de las llaves {} {this.methodName.bind (this)}


Tenemos que bind nuestra función con this para obtener la instancia de la función en clase. Me gusta así en ejemplo

<button onClick={this.sendContent.bind(this)}>Submit</button>

De esta manera, this.state será un objeto válido.


Tus funciones deben vincularse para jugar con estado o accesorios en controladores de eventos

En ES5, vincule sus funciones de controlador de eventos solo en el constructor pero no se vincule directamente en el render. Si vincula directamente en el renderizado, crea una nueva función cada vez que su componente se renderiza y vuelve a renderizar. Así que siempre debes vincularlo en constructor

this.sendContent = this.sendContent.bind(this)

En ES6, use las funciones de flecha

Cuando utiliza las funciones de flecha, no necesita hacer enlaces y también puede mantenerse alejado de los problemas relacionados con el alcance

sendContent = (event) => { }


bind(this) puede solucionar este problema, y ​​hoy en día podemos usar otras 2 formas de lograrlo si no le gusta usar bind .

1) Como es la forma tradicional, podemos usar bind(this) en el constructor, de modo que cuando usamos la función como devolución de llamada JSX, el contexto de this es la clase misma.

class App1 extends React.Component { constructor(props) { super(props); // If we comment out the following line, // we will get run time error said `this` is undefined. this.changeColor = this.changeColor.bind(this); } changeColor(e) { e.currentTarget.style.backgroundColor = "#00FF00"; console.log(this.props); } render() { return ( <div> <button onClick={this.changeColor}> button</button> </div> ); } }

2) Si definimos la función como un atributo / campo de la clase con función de flecha, ya no necesitamos usar bind(this) .

class App2 extends React.Component { changeColor = e => { e.currentTarget.style.backgroundColor = "#00FF00"; console.log(this.props); }; render() { return ( <div> <button onClick={this.changeColor}> button 1</button> </div> ); } }

3) Si usamos la función de flecha como devolución de llamada JSX, tampoco necesitamos usar bind(this) . Y aún más, podemos pasar los parámetros. Se ve bien, ¿no? pero su inconveniente es el problema del rendimiento, para más detalles, consulte ReactJS doco .

class App3 extends React.Component { changeColor(e, colorHex) { e.currentTarget.style.backgroundColor = colorHex; console.log(this.props); } render() { return ( <div> <button onClick={e => this.changeColor(e, "#ff0000")}> button 1</button> </div> ); } }

Y he creado un Codepen para hacer una demostración de estos fragmentos de código, espero que ayude.


this.changeContent debe estar vinculado a la instancia del componente a través de this.changeContent.bind(this) antes de pasarlo como el accesorio onChange , de lo contrario, la variable this en el cuerpo de la función no se referirá a la instancia del componente sino a la window . Ver Function::bind .

Cuando se usa React.createClass lugar de las clases ES6, todos los métodos que no son de ciclo de vida definidos en un componente se vinculan automáticamente a la instancia del componente. Ver Autobinding .

Tenga en cuenta que vincular una función crea una nueva función. Puede vincularlo directamente en render, lo que significa que se creará una nueva función cada vez que se renderiza el componente, o vincularlo en su constructor, que solo se activará una vez.

constructor() { this.changeContent = this.changeContent.bind(this); }

vs

render() { return <input onChange={this.changeContent.bind(this)} />; }

Las referencias se establecen en la instancia del componente y no en React.refs : debe cambiar React.refs.someref a this.refs.someref . También deberá vincular el método sendContent a la instancia del componente para que this refiera a él.