Home > front end >  How to tranfer "this" from one component to others?
How to tranfer "this" from one component to others?

Time:06-23

I have a component (Component1) which I want to call a function from another (in this case the main app). My question is why the onClickMe function can't access this from App component ?

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

the code example : https://codesandbox.io/s/react-playground-forked-99kumd?file=/index.js

CodePudding user response:

I will explain why this happens and add some comments, but first, here is the code that just works as you expect:

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

You were using "data" as a public class instance field - long story short your "data" assignment line ran before constructor body, so the onClickMe function was not bound yet when you assign it to action inside data object

Other stuff is not essential, it is just remarks.

You can declare onClickMe as arrow function, that is a special class field syntax that lets you get rid of .bind

You don't need "data" at all - you can just pass the props directly to child component. That way you would not have the problem of losing "this" in the first place:)

CodePudding user response:

Move the statement data = [{ name: "Giordano", action: this.onClickMe }], inside the constructor, in that way, this will refer to the App component, like this:

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

Here is the playground link.

  • Related