Ad

ReactJS: Properties Of Object Become Undefined After OnClick Call

- 1 answer

I've made a simple React app to display a number of image cards with quotes on them. It worked great, but then I refactored my code because I was getting a warning to use setState instead of directly altering the state properties. Then I started getting this error:

App.js:111 Uncaught TypeError: Cannot read property 'imageURL' of undefined

I guess this means that even if it was working before, I wasn't doing it properly. I suspect my problem is something to do with setState being asynchronous, but I'm having trouble finding a solution. Any light shed on the matter would be much appreciated!

Code below:

UPDATE:

Thanks for your suggestions! I have some updated code here, which is still returning the same error. Below are the up-to-date files as requested:

App.js: https://pastebin.com/daBTNKc7
App.css: https://pastebin.com/68xZdLfT
quotesData.json https://pastebin.com/1xD5DS0z

UPDATE 2:

I realised that I wasn't initialising this.state.cards in a sensible way, so the array was empty at a point when render() was trying to read it. I changed my constructor to initialise the array with some placeholder data, like this:

constructor(props) {
    super(props);

    this.state = {
        imagesLoaded: false,
        cards: [],
        numItems: 6,
        numLoaded: 0,
        quotes: quoteData
    };


    for (var i = 0; i < this.state.numItems; i++) {

        var quoteObject = ["Inspiring quote goes here.", "Speaker Name"];

        var cardObject = {
            imageURL: './src/placeholder.png',
            quote: quoteObject[0],
            credit: quoteObject[1]
        };

        this.state.cards.push(cardObject);

        //Sleep between requests so Unsplash doesn't block us for overloading it.
        this.sleep(500);
    }

    this.getRandomQuote = this.getRandomQuote.bind(this);
    this.renderCard = this.renderCard.bind(this);
}

It's working now - thanks everyone for your assistance! It still needs improving but hopefully I can make it the rest of the way on my own.

Ad

Answer

You have several errors. The following are not all of them, but it is a good start :)
Fix these, post the updated code, post also quotesData.json and App.css, and we will continue...

Start by fixing the following:

You cannot setState from the constructor.

Add the following after the constructor (and remove the call to refreshImages from the constructor).

componentDidMount() {  
  this.refreshImages();  
}

'this' is not set properly in the following:

getRandomQuote();  
renderCard();

Add the following inside your constructor:

this.getRandomQuote = this.getRandomQuote.bind(this);
this.renderCard = this.renderCard.bind(this);

After your updates, you are almost there :)

The problem that remains is that, although you set the cards value in the right place (componentDidMount), setState is asynchronous, so the first time you enter render(), cards are still not set.

I replaced the following line:

if (this.state.imagesLoaded) {

With the following:

if (this.state.cards.length === 0) {
  return (
    <div className="App">
      <header className="App-header">
        <h1 className="App-title">state.images still empty</h1>
      </header>
    </div>
  );  
} else if (this.state.imagesLoaded) {

And I see smart quotes with nice images :)

This is a workaround, of course, you need to decide what to do... Also, is sleep(500) in refreshImages really needed?

One more advice (not related to the problems you are having): use reactstrap, it will make your life easier. You need to know bootstrap for that, which is very easy to learn (but don't use bootstrap, use reactstrap).

Good luck with your work :)

Ad
source: stackoverflow.com
Ad