Tengo un componente (Component1) al que quiero llamar una función de otro (en este caso, la aplicación principal). Mi pregunta es por qué la función onClickMe no puede acceder a this desde el componente de la App .
class Component1 extends React.Component { render() { return ( <div> <h5>sub Component 1 </h5> <button onClick={() => this.props.data[0].action("foo")}> Click Me! </button> </div> ); } } class App extends React.Component { constructor(props) { super(props); this.state = { clicked: false }; this.onClickMe = this.onClickMe.bind(this); } data = [{ name: "Giordano", action: this.onClickMe }]; onClickMe(value) { /// I can get the correct value here console.log(value); /// THIS IS THE PROBLEM. /// "this" here refers to the object `data` not App /// so there is no 'setState' function on this this.setState({ clicked: true }); } render() { return ( <div> <h1>Main page</h1> <Component1 data={this.data} /> </div> ); } }el ejemplo de código: https://codesandbox.io/s/react-playground-forked-99kumd?file=/index.js
Explicaré por qué sucede esto y agregaré algunos comentarios, pero primero, aquí está el código que funciona como espera:
import React from "react"; import ReactDOM from "react-dom"; class Component1 extends React.Component { render() { return ( <div> <h5>sub Component 1 </h5> <button onClick={() => this.props.onClick("foo")}>Click Me!</button> </div> ); } } class App extends React.Component { constructor(props) { super(props); this.state = { clicked: false }; } // This syntax lets you get rid of .bind in constructor and just binds the method for you onClickMe = (value) => { console.log(this); console.log(value); this.setState({ clicked: true }); }; render() { return ( <div> <h1>Main page</h1> {/* It is better to pass props one by one, this way you can see what exactly you are passing */} <Component1 name="Giordano" onClick={this.onClickMe} /> </div> ); } } ReactDOM.render(<App />, document.getElementById("container"));Estaba usando "datos" como un campo de instancia de clase pública ; en pocas palabras, su línea de asignación de "datos" se ejecutó antes del cuerpo del constructor, por lo que la función onClickMe aún no estaba vinculada cuando la asignó a la acción dentro del objeto de datos.
Otras cosas no son esenciales, son solo comentarios.
Puede declarar onClickMe como función de flecha, que es una sintaxis de campo de clase especial que le permite deshacerse de .bind
No necesita "datos" en absoluto; simplemente puede pasar los accesorios directamente al componente secundario. De esa manera no tendrías el problema de perder "esto" en primer lugar :)
Mueva la declaración data = [{ name: "Giordano", action: this.onClickMe }] , dentro del constructor, de esa manera, this se referirá al componente de la App , así:
import React from "react"; import ReactDOM from "react-dom"; class Component1 extends React.Component { render() { console.log(this.props); return ( <div> <h5>sub Component 1 </h5> <button onClick={() => this.props.data[0].action("foo")}> Click Me! </button> </div> ); } } class App extends React.Component { constructor(props) { super(props); this.state = { clicked: false }; this.onClickMe = this.onClickMe.bind(this); this.data = [{ name: "Giordano", action: this.onClickMe }]; } // data = [{ name: "Giordano", action: this.onClickMe }]; onClickMe(value) { /// I can get the correct value here console.log("This ", this); /// THIS IS THE PROBLEM. /// "this" here refers to the object `data` not App /// so there is no 'setState' function on this this.setState({ clicked: true }); } render() { console.log(this.state.clicked); return ( <div> <h1>Main page</h1> <Component1 data={this.data} /> </div> ); } } ReactDOM.render(<App />, document.getElementById("container"));Aquí está el enlace del parque infantil.