Skip to content

Latest commit

 

History

History
607 lines (399 loc) · 18.8 KB

intro-to-fetching-data.md

File metadata and controls

607 lines (399 loc) · 18.8 KB

Title: Component LifeCycles
Duration: 1 - 1.5hrs
Creator: Joe Keohan

Intro to AJAX and HTTP Service

Note: If you haven't yet done so request a FREE api key from OMDB

Learning Objectives

  • Describe what are 3rd Party APIs and how they are used
  • Review Query Parameters and how they can be used in 3rd Party API requests
  • Set up fetch and make API request in a React App
  • Learn to work with ES8 Async/Await as a way to handle Promises used in fetch

What We Are Building

Here is a working version of the OMBd Movie App we will be building together.

Framing

The web is filled with data. Lots and lots of data. We make use of this data every time we perform a Google search or find ourselves on Amazon searching for a product.

This need for applications to query and then retrieve a set of results are the premise for working with API (Application Programing Interface) servers.

Were you ever curious how that data looks under the hook before it's been placed into HTML elements and beautifully styled?

I'm sure you have so let's take a look.


⏰ Activity - 2min

Take a moment to examine raw data sent from an API

https://randomuser.me/api

If successful we should see something similar to the following, provided the Parsed button is clicked.

Now click on the Raw button. In reality the data that is received is in a raw string format and it's up to the receiver to convert it back into it's true javascript data types.

Go back and view the data as Parsed and take a moment to examine the results

❓ - Try and answer the following questions:

  • What is the most top level JS datatype that contains all the data?
  • Which key contains the user data?
  • What is the full path to the users first name?

What is Serialized Data?

We confirmed that the Raw data sent via HTTP is a string and is done so because it's more efficient. We call data in this form "stringified". They are native data structures which are serialized into a string representation of the data.

Although the data is transmitted as a string it must be parsed back into it's native form for JS to make use of it.

There are two major serialized data formats...

JSON

JSON stands for JavaScript Object Notation and has become a universal standard for serializing native data structures for transmission. It is light-weight, easy to read and quick to parse.

{
  "users": [{ "name": "Bob", "id": 23 }, { "name": "Tim", "id": 72 }]
}

XML

XML stands for eXtensible Markup Language and is the granddaddy of serialized data formats (itself based on HTML). XML is fat, ugly and cumbersome to parse. It remains a major format, however, due to its legacy usage across the web. You'll probably always favor using a JSON API, if available.

<users>
  <user id="23">
    <name><![CDATA[Bob]]></name>
  </user>
  <user id="72">
    <name><![CDATA[Tim]]></name>
  </user>
</users>

Tools Of The Trade

jQuery was one of the first libraries to provide an easy way to make Asynchronous JavaScript and XML (AJAX) requests. Over time, new libraries were introduced, and with the introduction of ES6 the fetch api.

Two of the most popular tools to fetch data are:

Third Party APIs

Many web sites have their own data which they make available to developers either for free or a small fee, while others charge quite a bit and are very expensive.

Below is a non-exhaustive list of some free API's you can use. Please note that some may require signing up for an API key (Giphy) and others require a bit more setup (Marvel API)

No Key Required

  1. Pokemon: http://pokeapi.co/
  2. Card Deck: https://deckofcardsapi.com/
  3. Google Books: https://developers.google.com/books/
  4. City of Chicago: https://data.cityofchicago.org/
  5. Beer: https://www.brewerydb.com/developers
  6. Chuck Norris: http://www.icndb.com/
  7. Rick and Morty: https://rickandmortyapi.com/documentation/#rest
  8. Star Wars: https://swapi.co/

Key Required

  1. Marvel: https://developer.marvel.com/
  2. Weather: https://openweathermap.org/api
  3. Giphy: https://developers.giphy.com/
  4. News: https://newsapi.org/

API

As we've seen so far API's provide access to the data via a url. Let's take a look at the RandomUser API specifically.

https://randomuser.me/api

This returns a single random user.

Query String

This API has also been constructed to allow us to request multiple users in one call. This is done by using a query string that includes the search parameters.

The query string is placed at the end of the url and starts with a ? followed by a key=value pair.

Here are a few examples of API's using a query string to include additional parameters in the request:

Random User

Here we request 5 users

https://randomuser.me/api/?results=5

We can also add multiple search queries using the & symbol along with additional query values.

https://randomuser.me/api/?results=5&gender=female

Swapi (Star Wars)

https://swapi.dev/api/people/?search=r2

API Keys

Some API's require keys in order to request data. The keys are added to url along with any additional query parameters.

Here is an example of a request to OMDB (open movie data base), for a movie with the title Eraserhead and and apikey. The apikey may or may not work depending on if it is still active.

http://www.omdbapi.com/?t=Eraserhead&apikey=98e3fb1f

The order of the parameters is not important and we could have written it as follows:

http://www.omdbapi.com/?apikey=98e3fb1f&t=Eraserhead

The name of query parameter for the api key may be different depending on the API.

The below example uses apiKey as the key query param name.

News API

http://newsapi.org/v2/everything?q=bitcoin&apiKey=5d74ff271abd4bd3b2109d14d74c7d0b

While this API uses api_key

Giphy API

http://api.giphy.com/v1/gifs/search?q=ryan+gosling&api_key=CqhoGv4lcfGV4wOpxPVfbn0bXhmpHB9c&limit=5"

A Note On JSON

JSON stands for JavaScript Object Notation and is a subset of the object literal notation of JavaScript. It is primarily used to transfer data between client and server when requesting or sending data using an API.

When sending data the API server first converts the JS into a string and then its the responsibility of the client to convert it back into the original JS data type.

There are 2 rules that govern JSON.

  • all keys/values must use double quotes
  • trailing commas are forbidden (don't include a comma after the last key:value pair)

Below is an example of some of the JSON returned by OBMD. Notice how all key:value pairs include double quotes. Also make note that the last key:value pair do not have a trailing comma.

let movie = {
   "Title": "Eraserhead",
    "Year": "1977",
    "Website": "N/A",
    "Response": "True"
 }

There are also online tools that can help you validate JSON and confirm that it follow the above rules such as: jsonlint.com

JSON includes 2 methods that allow us to either convert an object to a string or parse it back into an object:

  • JSON.stringify() - converts JS to a string
  • JSON.parse() - converts a JS string back into its original format

The OMDB API first uses JSON.stringify() to convert the data into a string and then the JSON Formatter Chrome Extension converts it back into a object.

We can use these JSON methods in our own JS code when needed.

Let's test them out in DevTools and see what they return.

Our Data

let movie = {
  "Title": "Eraserhead",
   "Year": "1977",
   "Website": "N/A",
   "Response": "True"
}

Data Stringified

let stringifiedMovie = JSON.stringify(movie)
// this is now one long string of text
stringifiedMovie

=> "{\"Title\":\"Eraserhead\",\"Year\":\"1977\",\"Website\":\"N/A\",\"Response\":\"True\"}"

Data Parsed

let parsedMovie = JSON.parse(stringifiedMovie)
parsedMovie

=> {Title: "Eraserhead", Year: "1977", Website: "N/A", Response: "True"}

Mini Movie App

We're going to build a tiny React single page app that has a text input, a button and when a user inputs a movie, they will get a set of movies that match from OMDB.


⏰ Activity - 2min

Setup

  • Fork the following CodeSandbox Starter
  • Examine the Form component and determine if its setup as Controlled or Uncontrolled.

React Architecture

Here is the CodeSandbox Solution

Our React architecture will look like the following:

App
 |
  - Form
  - MovieInfo

Making Our API Call

We're going to be making requests to OMDB using fetch. We'll be viewing those results in Chrome's Console. Once we build out the functionality, we'll start incorporating our data into our web page.

Let's build our first fetch request.

fetch is a function that returns a JS Promise. A Promise is used to allow developers run code asynchronously.

fetch(someUrl)

fetch will retrieve the data and once resolved will pass it to the .then() method.

fetch(someUrl)
.then(res => res.json())

fetch requires 2 .then() methods. The first will parse the data from a string back into its js data type and the second will then allow us to work with the actual data.

fetch(someUrl)
 .then(res => res.json())
 .then(data => someFunction(data))

Using Fetch in App

Lets test that the ombdapi and our key works to fetch the data we need.

http://www.omdbapi.com/?apikey=98e3fb1f&t=Eraserhead

If successful then we should see the following:

The useEffect Hook

Now it's time to use the useEffect Hook which lets us perform side effects of making the initial API call.

A side effect is any application state change that is observable outside the called function other than its return value.

Several examples of side effects are:

  • Logging to the console
  • Making an API call
  • Calling setInterval/setTimeout

As we pointed out in the last lecture on React lifecycles, useEffect and be run in one of 3 ways:

  • useEffect(() => {}, []) - empty [] means this will only run once when the Component mounts
  • useEffect(() => {}) - no [] means this will run on every initial render and re-render
  • useEffect(() => {}, [someValueToMonitor]) - run on initial render and then only if the value has changed on every additional re-render

ComponentDidMount

Let's configure useEffect to run only once when the component mounts and fetch a movie of our choice.

In order for this to work properly we must make sure that movieTitle contains an initial value.

const [movieTitle, setMovieTitle] = useState('star wars')

And now we will setup useEffect as ComponentDidMount which runs only once when the component is mounted.

  useEffect(() => {

  }, [])  

Lets retrieve our data using the fetch API.

useEffect(() => {

  let movieUrl = `https://www.omdbapi.com/?t=${movieTitle}&apikey=98e3fb1f`;

  const makeApiCall = () => {
    fetch(movieUrl)
    .then(res => res.json())
    .then(data =>  {
      console.log('movieData', data)
      setMovieData((data))
     });
  }
  makeApiCall()

}, [])

We should see the following results in the console.

Rendering our response in the Browser

Let's update the MovieInfo component to include the elements for the data we want to render.

function MovieInfo(props){
    return  (
      <div>
        <h1>Title: </h1>
        <h2>Year: </h2>
        <img src='movie url' alt='movie'/>
        <h3>Genre: </h3>
        <h4>Plot: </h4>
      </div>
    )  
}

⏰ Activity - 10min

Examine the props that have been passed down and render the corresponding data for the element placeholders we've assigned.


Conditionally Rendering The MovieInfo Component

Finally, let's add some conditional logic that will either render MovieInfo and pass in our movie data as props or render nothing at all.

  return (
    <div className="App">
      <div>Best Movie App Ever</div>
      <Form handleSubmit={handleSubmit} />
      {movieData.Title ? <MovieInfo movie={movieData} /> : null}
    </div>
  );

However a more terse way to write the conditional logic is as follows:

  return (
    <div className="App">
      <div>Best Movie App Ever</div>
      <Form handleSubmit={handleSubmit} />
      {movieData.Title && <MovieInfo movie={movieData} /> }
    </div>
  );

ComponentDidUpdate

Since our Form is responsible for capturing the user input and using that input as our search criteria let's make sure it updates movieTitle.

  const handleSubmit = title => {
    console.log('App - makeApiCall - title', title);
    setMovieTitle(title)
  };

As we can see no movie data is being fetched when the user submits a new query.

So let's configure useEffect to re-run anytime the movieTitle has been updated.

To do this we will add a value to the useEffect dependency array.

useEffect(() => {
  let movieUrl = `https://www.omdbapi.com/?t=${movieTitle}&apikey=98e3fb1f`;

  const makeApiCall = () => {
    fetch(movieUrl)
    .then(res => res.json())
    .then(data =>  {
      console.log('movieData', data)
      setMovieData((data))
     });
  }
  makeApiCall()

}, [movieTitle])

Solution CodeSandbox

Up to this point we've covered quite a few ES6 features however many more features have been introduced with ES7, ES8, ES9 and the most recent, ES10.

This article provides a synopsis of all the new features released up to ES9.

  • Let's take a look at the ES6 section
  • Browse down the article until you hit the ECMAScript 6(2015) section
  • How many of those features have you already been introduced to?
  • When asked slack your response in a thread created by the instructor

The newest feature will be working with today is ES8 - Async Functions

Async / Await

The keyword await makes JavaScript wait until a line of code completely finishes executing. However, you can only use await inside of an async function. How do we turn a function into an async function?

Easy! We just put the word async in front of the word function. With arrow functions, we just write async before the parenthesis, like this:

async () => { etc...}

So, if we use async and await we can refactor our code and remove the .then() methods.

In order to force the code to run asynchronously we then add the keyword await before and statement that returns a Promise. In the example below both fetch and res.json() return Promises and must be preceded by the keyword await

useEffect(() => {
  let movieUrl = `https://www.omdbapi.com/?t=${movieTitle}&apikey=98e3fb1f`;

  const makeApiCall = async () => {
    const res = await fetch(movieUrl)
    const json = await res.json()
    setMovieData(data)
  }

  makeApiCall()

}, [movieTitle])

⏰ Activity - 10min

Perform the following:

  • Sign up for API keys for the API's listed below
  • Using your browser confirm that you are able to retrieve data from the API's

API's


Resources: