In this componentDidUpdate method, after performing setState to set quotes to what's returned from the fetch, I have to use the callback to perform setState a second time to set randomQuoteIndex to the result of calling randomQuoteIndex, which relies on this.state.quotes.length
, i.e.:
componentDidMount() {
fetch('https://gist.githubusercontent.com/nataliecardot/0ca0878d2f0c4210e2ed87a5f6947ec7/raw/1802a693d02ea086817e46a42413c0df4c077e3b/quotes.json')
// Takes a JSON response string and parses it into JS object
.then(response => response.json())
// state is set to quotes: quotes due to destructuring
// Using setState callback since setState is asynchronous and need to make sure quotes is loaded before setting the randomQuoteIndex state since it depends on it
.then(quotes => this.setState({ quotes }, () => {
this.setState({
randomQuoteIndex: this.randomQuoteIndex(),
isDoneFetching: true
})
}))
}
Why doesn't the code below work? Based on the selected answer to this question, I'm under the impression that the second item in setState won't be applied until after state is set for the first item. If I try this, I get an error "TypeError: Cannot read property 'quote' of undefined." (I read that setState is asynchronous and about when to use the callback but I'm having a hard time understanding what I read/how it applies in this case.)
componentDidMount() {
fetch('https://gist.githubusercontent.com/nataliecardot/0ca0878d2f0c4210e2ed87a5f6947ec7/raw/1802a693d02ea086817e46a42413c0df4c077e3b/quotes.json')
// Takes a JSON response string and parses it into JS object
.then(response => response.json())
// Using setState callback since setState is asynchronous and need to make sure quotes is loaded before setting the randomQuoteIndex state since it depends on it
.then(quotes => this.setState({
quotes,
randomQuoteIndex: this.randomQuoteIndex(),
isDoneFetching: true
}));
}
Here's the full component code (the working version):
import React, { Component } from 'react';
import './App.css';
import { random } from 'lodash';
import Button from './components/Button';
class App extends Component {
constructor(props) {
super(props);
this.state = {
quotes: [],
randomQuoteIndex: null,
isDoneFetching: false
}
}
componentDidMount() {
fetch('https://gist.githubusercontent.com/nataliecardot/0ca0878d2f0c4210e2ed87a5f6947ec7/raw/1802a693d02ea086817e46a42413c0df4c077e3b/quotes.json')
// Takes a JSON response string and parses it into JS object
.then(response => response.json())
// state is set to quotes: quotes due to destructuring
// Using setState callback since setState is asynchronous and need to make sure quotes is loaded before setting the randomQuoteIndex state since it depends on it
.then(quotes => this.setState({ quotes }, () => {
this.setState({
randomQuoteIndex: this.randomQuoteIndex(),
isDoneFetching: true
})
}))
}
get randomQuote() {
return this.state.quotes[this.state.randomQuoteIndex];
}
randomQuoteIndex() {
return random(0, this.state.quotes.length - 1);
}
render() {
return (
<div className="App" id="quote-box">
{this.state.isDoneFetching ? this.randomQuote.quote : 'Loading...'}
<Button
buttonDisplayName="Next"
clickHandler={this.blah}
/>
</div>
);
}
}
export default App;