简体   繁体   中英

how do I use "this" on an component function in react?

I need to call this.getFact from the Animal component, but using this raises TypeError: this is undefined

import './App.css';
import React from 'react';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      fact: '',
    }
  }

  getFact(endpoint) {
    fetch(endpoint)
      .then(response => response.json())
      .then(data => {
        this.setState({
          fact: data.text,
        });
      })
  }

  Animal(props) {
    return (
      <div>
        <h1>{props.name}</h1>
        <button onClick={() => this.getFact(props.endpoint)}>get a {props.name.toLowerCase()} fact</button>
      </div>
    )
  }

  render() {
    return (
      <div className="App">
        <div>
          <p>{this.state.fact}</p>
        </div>
        <div className="animals">
          <this.Animal name="Dog" endpoint="https://some-random-api.ml/facts/dog" />
          <this.Animal name="Cat" endpoint="https://some-random-api.ml/facts/dog" />
          <this.Animal name="Bird" endpoint="https://some-random-api.ml/facts/dog" />
          <this.Animal name="Otter" endpoint="https://some-random-api.ml/facts/dog" />
        </div>
      </div>
    );
  }
}

export default App;

You need to bind both of the functions (getFact and Animal) to be able to use this inside the Animal function. In your constructor do this:

  constructor(props) {
    super(props);
    this.state = {
      fact: ""
    };

    // Here you will bind the functions so they can be callable with this
    this.getFact = this.getFact.bind(this);
    this.Animal = this.Animal.bind(this);
  }

That will solve the issue but I will suggest to move the Animal component outside of your class and pass the getFact as a prop. you still need to bind the getFact function, but honestly it will be more react like and in the long run is more maintainable. Something like this

function Animal(props) {
  return (
    <div>
      <h1>{props.name}</h1>
      <button onClick={() => props.getFact(props.endpoint)}>
        get a {props.name.toLowerCase()} fact
      </button>
    </div>
  );
}

class AppReactWay extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      fact: ""
    };
    this.getFact = this.getFact.bind(this);
  }

// Same code as you have

  render() {
    return (
      <div className="App">
        <div>
          <p>{this.state.fact}</p>
        </div>
        <div className="animals">
          <Animal
            name="Dog"
            getFact={this.getFact}
            endpoint="https://some-random-api.ml/facts/dog"
          />
        </div>
      </div>
    );
  }
}

export default App;

Also after checking the API response you need to modify the getFact function, is not data.text, it is data.fact.

  getFact(endpoint) {
    fetch(endpoint)
      .then((response) => response.json())
      .then(({ fact }) => {
        this.setState({
          fact
        });
      });
  }

Here's a working sandbox with both examples.

The better approach would be to directly call {this.Animal({ name: 'Dog', endpoint: 'https://some-random-api.ml/facts/dog'})} and so on for every animal.

The best approach would be for Animal to have it's own component and pass getFact function as param, don't forget you will need to bind it in that case or it will loose this context.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM