Reputation: 994
class ProductsIndex extends Component {
constructor (props){
super(props);
console.log(this) // #1. this logs ProductsIndex component
fetch('someUrl')
.then(res => res.json())
.then(res => console.log(this)) // #2. this logs ProductsIndex component
fetch('someUrl')
.then(res => res.json())
.then(console.log) // #3. this logs [{..},{..},{..},{..}]
}
componentDidMount(){
fetch('someUrl')
.then(res => res.json())
.then(console.log) // #4. this logs [{..},{..},{..},{..}]
}
As shown in the code above, both #1 and #2 point to same this. And also as shown, both #3 and #4 returns same array. However, why the code below doesn't work??
class ProductsIndex extends Component {
constructor (props){
super(props);
fetch('someUrl')
.then(res => res.json())
.then(arr => {
this.state = {
array: arr
}
})
}
It throws an error saying that this.state is null and I really don't understand why.
Below code is the solution. could anyone please explain what exactly the difference is??
class ProductsIndex extends Component {
constructor (props){
super(props);
this.state = {
array: []
}
}
componentDidMount(){
fetch('someUrl')
.then(res => res.json())
.then(arr => {
this.setState({
array: arr
})
})
}
Upvotes: 19
Views: 16087
Reputation: 281656
The Problem is that when you place an async request in constructor
, the promise
might be resolved after render
phase has executed and at that point this.state is null and also since you just assign
this.state = {
array: arr
}
and this won't lead to a re-render and so the component won't reflect the change. Having said that you should place your async requests in componentDidMount
which you mention in your second attempt, and since you call setState
there, a re-render
is triggered and the state in reflected in render
Upvotes: 16