React setState not working

Tell us what’s happening:
Tldr: The setState is not working in getNewQuote(). Line 50 in the JS editor.

https://codepen.io/dletulle/pen/mdOWbGJ?editors=0011

First off, I want to say after finishing the Front End Library section(apart from the projects), I felt really overwhelmed trying to make sense of how to put everything together into an actual project. I plan to make my own project, but I was watching a YouTube video of some one going through their solution. In the “getNewQuote” section, they use an API to fetch the data and set the state of the text and authors, which does work. I thought I would instead make my own array and set the state from there, but it doesn’t work. Now I’ve even tried setting the state manually to a string, but it doesn’t set the state. Any idea what the issue is?
Your code so far

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36.

Challenge: Build a Random Quote Machine

Link to the challenge:

Hi @dLetulle !

You haven’t set an onclick function for the button.
When you do that then you can see the state update when the button is clicked.

1 Like

Yep, that’s a completely normal feeling.

1 Like

Ahh Thank you, I see that fixes it.
But just so I understand… since I included a call to the function on line 48, shouldn’t it have been updated from the start? That’s what confused me, because in the video example, when it was set to an API, it loads a random quote at the start because of line 48. Any idea why it loaded with the API and not when manually set?

In this case, you created a function but it was never called so that is why it never executed.

Now when the button is clicked then the function is called and it gets executed.

As for the API, I didn’t use that approach for this project.
Someone else will have a better answer on how that is working better than me.

Just to be clear, when I called “this.getNewQuote();” at the bottom of the QuoteBox class, that doesn’t count as calling the function?

If you are referring to this line here

  handleClick() {
    this.getNewQuote();
  }

Inside the handleClick function we are calling getNewQuote. But you have to call the handleClick function in order to execute that.

I’m referring to here, underneath the bindings.

Capture

I would also point out that you don’t seem to need handleClick - you can just call getNewQuote from the onClick event and cut out the middle man. Unless, that is, you have plans to add more to handleClick.

1 Like

I think it is because it is still inside the constructor

  constructor(props) {
    super(props); 
    this.state = {
      quoteText: "Initial state that should be updated.",
      quoteAuthor: "",
      curColor: this.props.color,
      tweetUrl: "https://twitter.com/intent/tweet/?text="
    }
    
    this.getNewQuote = this.getNewQuote.bind(this);
    this.handleClick = this.handleClick.bind(this);
    this.getNewQuote();
  }

Right, it is inside the constructor, so it gets called once, as the component mounts.

1 Like

Hmm… I believe it is inside the constructor. With the function call in the exact same place, it works if I replace it with the API version the person I was following used. So I’m not sure what the issue is. It’s a not a big deal, was just bothering me because I don’t understand why it doesn’t work.

This is their version with API:

  getNewQuote(){
    const app = this;
    fetch('https://api.quotable.io/random')
    .then((response) => response.json())
    .then((data) => {
      app.setState({
        quoteText: data.content,
      });
    });
  }

Yeah, they are making an api call because they are getting the data from a server. You don’t need that because you are hardcoding your quotes.

Hmm… I believe it is inside the constructor.

In the code at which I am looking, you are definitely calling getNewQuote in your constructor. That kind of makes sense, but I probably would have called it in componentDidMount, but I’m not sure what best practice is. In any case, yours should work.

So I’m not sure what the issue is. It’s a not a big deal, was just bothering me because I don’t understand why it doesn’t work.

What is the “it” in that sentence? The code that I see in your pen works the way I would expect. What code do you think isn’t working?

In the getNewQuote function, it should set the state, which is does when called by pressing the button. BUT, from my understanding, since I’ve called the getNewQuote function in my constructor, it should set it as soon as I load the page without having to press anything. When I swap out my code for the API version, it does just that, grabs a random quote and sets it upon loading without having to click the button.

I know I could just set the initial state of the constructor to be a random quote and avoid this, but I still just don’t understand why the page sets the constructor on load with API and not the way I’m doing it.

Oh, IC.

Yeah, I think there is an issue with calling setState inside the constructor. Normally you just manually set the state in the constructor, like you are doing a few lines above. When the constructor is running, the component hasn’t finished “constructing”.

Try putting it in componentDidMount:

  componentDidMount() {
    this.getNewQuote();
  }

instead of the constructor. It gets run immediately after the component is done mounting. Either that or separate out the logic of generating a random quote and you can initialize the state with that.

1 Like

Yeah, my suspicion was right - from the docs:

You should not call setState() in the constructor() . Instead, if your component needs to use local state, assign the initial state to this.state directly in the constructor … Avoid introducing any side-effects or subscriptions in the constructor. For those use cases, use componentDidMount() instead.

That’s kind of what I’m saying. I think either solution works.

2 Likes

Awesome, I understand now! Thanks for the explanation!

1 Like