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;
CodePudding user response:
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.
CodePudding user response:
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.
