Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Adam Beer
Adam Beer
11,314 Points

React with instagram api

I build a photo page with React to my friend and I use instagram api. Now I get back an array. There are 10 photos in the array. How do I display the pictures I received? Now the bug -> TypeError: Cannot read property 'map' of undefined Any idea? Unfortunately I got stuck.

The recovered pictures.

Inside the array.

So I can use the index property, but how?

import React from 'react';
import axios from 'axios';

class Instagram extends React.Component {
    state = {
        images: [],
    };

    componentDidMount() {
        let token = 'access-token';
        let num_photos = 10;


        axios.get('https://api.instagram.com/v1/users/self/media/recent/?access_token=' + token + '&count=' + num_photos)
            .then(res => {
                console.log(res.data.data);
                this.setState({ images: res.data.data });
            })
            .catch(err => {
                console.log(err)
            })

    }

    render() {
        return(
            <div className="instafeed">
                <ul>
                    { this.state.images.map((image) => 
                    {image}
                    )};
                </ul>
            </div>
        )
    }
}

export default Instagram;

3 Answers

Ari Misha
Ari Misha
19,323 Points

Well, I've managed to find out a couple of errors in your code. First off, you can't use the semi-colon in the return of render method. Secondly, according to the error, this.state.images is undefined coz your api is async and render fires off without any data in your this.state.images. You either have to put in a Loader, or put the logic of fetching the data in componentWillRecieveProps lifecycle method. You could also forcefully update as soon as the images are set.

I hope it helps!

~ Ari ~

Adam Beer
Adam Beer
11,314 Points

Thank you for your response! Sorry, but I am a bit comfused. How can use componentWillRecieveProps lifecycle method in my code?

Seth Kroger
Seth Kroger
56,413 Points

One option is to use a ternary/conditional operator to check it the images array is loaded yet. The setState call should be sufficient to update the component when the axios call is finished.

render() {
  return (
    <div className="instafeed">
      { 
        this.state.images 
          ? ( /* your gallery code */  )
          : ( /* blank space or loading graphic */ )
      }
    </div>
  );
}
Seth Kroger
Seth Kroger
56,413 Points

I should also note you should be careful when returning an object from an arrow function. Because a curly-brace right after the arrow is always interpreted as the start of a multi-line block with an explicit return, you should always wrap an object in parentheses.

this.state.images.map((image) => {image}) // returns nothing vs.

this.state.images.map( (image) => ({image}) ) 
  // though you probably want something like
this.state.images.map( (image) =>  (<li>{image}</li>) )
Mike Hatch
Mike Hatch
14,940 Points

Seth Kroger, also, starting at Line 5 - 10 shouldn't Adam's code read like this:

  constructor(props) {
    super(props)

    this.state = {
      images: []
    }
  }

Or no? Or maybe he has no properties to pass.

Ari Misha
Ari Misha
19,323 Points

Mike Hatch Well, the code looks fine to me. Your way is more recommended but Adam's way is as programmatically correct as initializing state in the constructor. And @SethKroger I'm not sure if it's the best way to put a Loader in the return of the render. Instead, we could go for the traditional if-else blocks in the render method or have a Utility Higher Order Component to render either a Loader or the wrapped Component itself, kinda like Branch in the recompose, right?

Mike Hatch
Mike Hatch
14,940 Points

Hi Ari, thanks for commenting. Based on further research, I guess either way is the right way. The React docs show it as the constructor/super way. It seems to me a choice that developers make. Sometimes I think JavaScript is just too flexible for its own good. There shouldn't be all this extra cognitive overload. One way or the other please!

Adam Beer
Adam Beer
11,314 Points

Hi guys! Thanks your helps! You gave me very useful answers! Now the page not beautiful, but instagram get the pictures back. I'm very happy now! Thank you so much! Have a nice day!

Printscreen

import React from 'react';
import axios from 'axios';

class Instagram extends React.Component {
    state = {
        images: [],
    };

    componentDidMount() {
        let token = 'access-token';
        let num_photos = 10;


        axios.get('https://api.instagram.com/v1/users/self/media/recent/?access_token=' + token + '&count=' + num_photos)
            .then(res => {
                console.log(res.data.data[0].images);
                    this.setState({ images: res.data.data });
            })
            .catch(err => {
                console.log(err)
            })

    }

    render() {
        return(
            <div className="instafeed">
                { this.state.images.map((image) => {
                    return <img src={image.images.thumbnail.url} />
                })}
            </div>
        )
    }
}

export default Instagram;