Search code examples
reactjsfetchreact-state-management

React state doesn't seem to be fetching api and setting state before render


I'm trying to fetch an api of quotes and populate the react component with the first one. Later I'll use the button to pick a random one. I'm just now learning react, my react tutorial in freecodecamp didn't show anything about fetch so I found the code to pull these quotes online. If I add another callback after the this.setState I can console.log and see all the arrays but even with the if statement in the render it doesn't seem to be there in the state to render. What am I missing about setting the state or getting the component to render after the state has set to the array. I have already looked at this stackoverflow question.

class Quotes extends React.Component{
  constructor(props){
    super(props)
    this.state = {
      quotes: []
    }
  }
  
  componentDidMount() {
      fetch("https://type.fit/api/quotes")
      .then((response) => response.json())
      .then(quotesList => {
          this.setState({ quotes: quotesList });
      });
  }
  
  render(){ 
    if (!this.state.quotes) {
            return <div />
        }
    
    return(
      <div>
        <p id="text">{this.state.quotes[0].text}</p>
        <p id="author">{this.state.quotes[0].author}</p>
        <div id="buttons">
          <button id="new-quote">New Quote</button>
          <a id="tweet-quote" href="#"><i className="fa-brands fa-twitter"></i></a>
        </div>
      </div>
    );
  }
}

class QuoteBox extends React.Component{
  constructor(props){
    super(props)
  }
  
  render(){
    return(
      <div id="quote-box">
         <Quotes />
      </div>
      
    );
  } 
}

ReactDOM.render(<QuoteBox />, document.getElementById('page-wrapper'))
#page-wrapper{
  
  #quote-box{
    display:flex;
    height:100vh;
    justify-content:center;
    align-items:center;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="page-wrapper">
  
</div>


Solution

  • An empty array [] is not a falsy value, consequently your if does not get triggered and an out of bounds array access is done. Check for array length in your if instead and it will work.

    See this thread on StackOverflow which covers truthy and falsy values.

    Here your code with the condition within the if changed to this.state.quotes.length === 0.

    class Quotes extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          quotes: [],
        };
      }
    
      componentDidMount() {
        fetch("https://type.fit/api/quotes")
          .then((response) => response.json())
          .then((quotesList) => {
            this.setState({ quotes: quotesList });
          });
      }
    
      render() {
        // check for array length here
        if (this.state.quotes.length === 0) {
          return <div>Fetching data...</div>;
        }
    
        return (
          <div>
            <p id="text">{this.state.quotes[0].text}</p>
            <p id="author">{this.state.quotes[0].author}</p>
            <div id="buttons">
              <button id="new-quote">New Quote</button>
              <a id="tweet-quote" href="#">
                <i className="fa-brands fa-twitter"></i>
              </a>
            </div>
          </div>
        );
      }
    }
    
    class QuoteBox extends React.Component {
      constructor(props) {
        super(props);
      }
    
      render() {
        return (
          <div id="quote-box">
            <Quotes />
          </div>
        );
      }
    }
    
    ReactDOM.render(<QuoteBox />, document.getElementById("page-wrapper"));
    #page-wrapper{
      
      #quote-box{
        display:flex;
        height:100vh;
        justify-content:center;
        align-items:center;
      }
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
    <div id="page-wrapper">
      
    </div>