React: Working With API’s, Mapping Arrays & Looping Images

Series

This is part 1/3 in a series making a responsive masonry image grid in React, with images pulled from Unsplash using an API, queried from a text input in a small sample app.

Making An API Request

React doesn’t actually have any built in functionality for working with API so we will be looking at how to integrate common methods of fetching data INTO React and how those items work together.

Common ways to accomplish this include using fetch() or Axios. We will start by going over the process with Axios.

Get it installed in your project and then come back.

From there is actually really simple to make a basic query. We have our basic App component that we are working with here that is getting an input fed into it from the SearchBar child component.

Then we have our function onSearchSubmit that executes when the user submits their query to the text input child component. We call the .get method on axios, and then following the axios and Unsplash API documentation we submit the parameters of our request and our authorization in the header. I’ve redacted my authorization key here. Later on we will be replacing it with an environment variable.

class App extends React.Component {
  onSearchSubmit(input) {
    axios.get("https://api.unsplash.com/search/photos", {
      params: {
        query: input,
      },
      headers: {
        Authorization: "Client-ID your-authorization-key",
      },
    });
  }

  render() {
    return (
      <div className="ui container" style={{ marginTop: "10px" }}>
        <SearchBar onSubmit={this.onSearchSubmit} />
      </div>
    );
  }
}

unsplash api request

Working With Returned Data

.then()

Because axios works with Promises we are able to use the .then method as one way to do something with our retrieved data.

class App extends React.Component {
  onSearchSubmit(input) {
    axios
      .get("https://api.unsplash.com/search/photos", {
        params: {
          query: input,
        },
        headers: {
          Authorization:
            "Client-ID your-authorization-key",
        },
      })
      .then((response) => {
        console.log(response.data.results);
      });
  }

  render() {
    return (
      <div className="ui container" style={{ marginTop: "10px" }}>
        <SearchBar onSubmit={this.onSearchSubmit} />
      </div>
    );
  }
}

You can read more about the .then method here:

MDN: Promise.prototype.then()

returned data in array

Async Await

This would be my preferred method, which is to use Async Await. We simply convert the function to an Async function and then await the response from the API.

class App extends React.Component {
  async onSearchSubmit(input) {
    const response = await axios.get("https://api.unsplash.com/search/photos", {
      params: {
        query: input,
      },
      headers: {
        Authorization: "Client-ID your-authorization-key",
      },
    });

    console.log(response.data.results);
  }

  render() {
    return (
      <div className="ui container" style={{ marginTop: "10px" }}>
        <SearchBar onSubmit={this.onSearchSubmit} />
      </div>
    );
  }
}

returned data in array

Set State

The next thing to do is initialize state with a blank array of images, and then set the state of that array to the array of photos once it has arrived. Note that we also have to refactor the onSearchSubmit function with an arrow function to avoid the “this is undefined”.

Then in our render method we can output the number of images that are found to get some initial feedback up on our screen before we make our display loop.

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

  onSearchSubmit = async (input) => {
    const response = await axios.get("https://api.unsplash.com/search/photos", {
      params: { query: input },
      headers: {
        Authorization: "Client-ID your-authorization-key",
      }
    });

    this.setState({ images: response.data.results });
  }

  render() {
    return (
      <div className="ui container" style={{ marginTop: "10px" }}>
        <SearchBar onSubmit={this.onSearchSubmit} />
        Found: {this.state.images.length} images
      </div>
    );
  }
}

input field response

Creating Custom Axios Client

Before moving on let us do a bit of a refactor here. We have a lot of configuration code inside of our App component which is bad practice. Let’s create a custom client for the Unsplash API. First we can make a new directory in our base index called API and we can put any API configuration files inside of that directory.

custom client file structure

inside our custom unsplash client we can place the following

import axios from 'axios';

export default axios.create({
    baseURL: 'https://api.unsplash.com',
    headers: {
        Authorization: "Client-ID S4bzmlXtaBM7r_gDs6gVZDxsADGoS7jvMxbsfI7aueI",
      }
});

And then we can import this client into the App component and remove the configuration data from the component.

import React from "react";
import unsplash from "../API/unsplash";
import SearchBar from "./SearchBar";

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

  onSearchSubmit = async (input) => {
    const response = await unsplash.get("/search/photos", {
      params: { query: input },
    });

    this.setState({ images: response.data.results });
  };

  render() {
    return (
      <div className="ui container" style={{ marginTop: "10px" }}>
        <SearchBar onSubmit={this.onSearchSubmit} />
        Found: {this.state.images.length} images
      </div>
    );
  }
}

export default App;

We could have placed the whole request inside of the custom client, but this structure will serve us better when we get to more complicated projects.

Looping Over and Displaying the Image Array

Passing in the Array

First we need to scaffold out a new component which we will call ImageList. Then we need to pass our array of images that we have received from unsplash into this component as a prop, and then do… something. Let’s go ahead and get that array passed into the new component first.

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

  onSearchSubmit = async (input) => {
    const response = await unsplash.get("/search/photos", {
      params: { query: input },
    });

    this.setState({ images: response.data.results });
  };

  render() {
    return (
      <div className="ui container" style={{ marginTop: "10px" }}>
        <SearchBar onSubmit={this.onSearchSubmit} />
        <ImageList images={this.state.images} />
      </div>
    );
  }
}

Looking at our render method we can see the new component ImageList that we have created and the prop called images where we have passed in the array. Now lets head over to our new component and manipulate that array.

Looping Over the Array With .map

The .map method is a vanilla JS method that creates a new array, by taking an array, looping over it and manipulating each item in the array somehow. In this case we want to create a new array where every item in the array is an image with the URL of the image that we want.

We then take that new array called images and return it for display in the App component.

const ImageList = (props) => {
    console.log(props.images);
  const images = props.images.map((image) => {
    return <img src={image.urls.small} />;
  });

  return <div>{images}</div>;
};

console logged image array iimages output on screen array