<info 340/>
AJAX
Joel Ross
Autumn 2024
View of the Day
-
Q&A
-
Firebase Hosting (demo)
-
AJAX ("lecture")
-
fetch() and Promises (code demo)
-
Effect Hooks (code demo)
Questions?
Hosting on Firebase
GitHub pages is not able to cleanly handle client-side routing, so we'll use Firebase to host your projects instead!
Firebase is a web backend solution; it provides multiple features which you can access without need to "code" them yourselves.
- Web Hosting
- Databases
- User Authentication
next week
React Deployment to Firebase
Use a combination of firebase command line tools and create-react-app scripts to build and deploy your React application to Firebase hosting!
See Project Draft 2 instructions on Canvas for details.
Work on your app with all data being stored in the state (even if not persisted). If you have a clean state-based interactive structure, adding in the database will be straightforward.
Until we go over Firebase...
function App(props) {
const [data, setData] = useState([]); //define the state
//respond to events
const handleClick = (event) => {
setData(newData); //update state when event occurs
}
//render the state
const dataElems = data.map((item) => {
return <DataItem value={item} />
})
return (
<div>{dataElems}</div>
)
}
HTTP Requests
protocol
domain
resource
"Hi Wikipedia, I'd like you to send me the Informatics page!"
two
t
(how to handle info)
(who has info)
(what info you want)
Browsers submit HTTP requests to a server when you follow a hyperlink or submit a form.
Form Attributes
We use attributes on a <form> to specify the HTTP Request
<form role="form" method="GET" action="/signup">
<label for="unameBox">Name:</label>
<input type="text" name="username" id="unameBox">
<button type="submit">Sign up!</button>
</form>
GET:
(default) sends a GET request; data is appended to the URI as a
query parameter (e.g.,
?username=value
)
POST:
sends a POST request; data is included in the request body.
The URI
HTTP Verb
A technique for having code (JavaScript) send an HTTP Request, rather than the browser
XML
EXtensible Markup Language
A generalized syntax for semantically defining structured content (HTML with own tags!)
<person>
<firstName>Alice</firstName>
<lastName>Smith</lastName>
<favorites>
<music>jazz</music>
<food>pizza</food>
</favorites>
</person>
XML
JSON
<breakfast_menu>
<food>
<name>Belgian Waffles</name>
<price>$5.95</price>
<description>
Two of our famous Belgian Waffles with plenty of real maple syrup
</description>
<calories>650</calories>
</food>
<food>
<name>Strawberry Belgian Waffles</name>
<price>$7.95</price>
<description>
Light Belgian waffles covered with strawberries and whipped cream
</description>
<calories>900</calories>
</food>
<food>
<name>Berry-Berry Belgian Waffles</name>
<price>$8.95</price>
<description>
Light Belgian waffles covered with an assortment of fresh berries and whipped cream
</description>
<calories>900</calories>
</food>
<food>
<name>French Toast</name>
<price>$4.50</price>
<description>
Thick slices made from our homemade sourdough bread
</description>
<calories>600</calories>
</food>
<food>
<name>Homestyle Breakfast</name>
<price>$6.95</price>
<description>
Two eggs, bacon or sausage, toast, and our ever-popular hash browns
</description>
<calories>950</calories>
</food>
</breakfast_menu>
{
"breakfast_menu": {
"food": [
{
"name": "Belgian Waffles",
"price": "$5.95",
"description": "Two of our famous Belgian Waffles with plenty of real maple syrup",
"calories": "650"
},
{
"name": "Strawberry Belgian Waffles",
"price": "$7.95",
"description": "Light Belgian waffles covered with strawberries and whipped cream",
"calories": "900"
},
{
"name": "Berry-Berry Belgian Waffles",
"price": "$8.95",
"description": "Light Belgian waffles covered with an assortment of fresh berries and whipped cream",
"calories": "900"
},
{
"name": "French Toast",
"price": "$4.50",
"description": "Thick slices made from our homemade sourdough bread",
"calories": "600"
},
{
"name": "Homestyle Breakfast",
"price": "$6.95",
"description": "Two eggs, bacon or sausage, toast, and our ever-popular hash browns",
"calories": "950"
}
]
}
}
A technique for having code (JavaScript) send an HTTP Request, rather than the user
JSON
XMLHttpRequest
AJAX requests are built on a browser-provided object called
XMLHttpRequest
. We don't use this method because it is overly verbose and complicated.
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4 && xhttp.status == 200) {
// Action to be performed when the document is read;
var xml = xhttp.responseXML;
var movie = xml.getElementsByTagName("track");
//...
}
};
xhttp.open("GET", "filename", true);
xhttp.send();
fetch()
The modern method for submitting XmlHttpRequests. Included in the DOM's API.
//send an AJAX request to the given url
fetch('url');
Not supported by all browsers! http://caniuse.com/#search=fetch Can add API features to browsers that do not yet support them by including a polyfill: an external library (code) that replicates that API.
https://github.com/github/fetch, or install whatwg-fetch library
//in index.js
import 'whatwg-fetch'
Asynchronous
AJAX requests are asynchronous, so happen simultaneously with the rest of the code.
That means that after the request is sent, the next line of code is executed without waiting for the request to finish!
console.log('About to send request');
//send request for data to the url
fetch(url);
console.log('Sent request');
(1)
(2)
(3)
(4) Data is actually received some time later,
and Promise is fulfilled
does NOT return the data,
but a Promise for it
Promises
Promises
We use the
.then()
method to specify a
callback function to be executed when the promise is
fulfilled (when the
asynchronous
request is finished)
//what to do when we get the response
function successCallback(response) {
console.log(response);
}
//when fulfilled, execute the callback function
//(which will be passed the http response)
const promise = fetch(url);
promise.then(successCallback);
//more common to use anonymous variables/callbacks:
fetch(url).then(function(response) {
console.log(response);
});
reads like English?
callback will be passed the request response
fetch()
Responses
The parameter fetch() passes to its .then() callback is the http response, not the data itself!
The response to an HTTP request (such as from fetch()) has two parts:
-
Header with information about the response. Like a postal envelope.
-
Body with the content (data) of the response. Like a postal letter.
HTTP Response Codes
Encoding the Body
The parameter passed to the
.then()
callback is the
response, not the data we're looking for.
So we need to extract the data from that response.
The fetch() API provides a method .json() that we can use to encode the data from the response into a readable format... but this method is also asynchronous and returns a promise!
fetch(url).then(function(response) {
const newPromise = response.json();
//... what now?
});
not the data
another promise
Returning Promises
If the
.then()
callback itself returns a Promise, then the "original" promise will take on the status and data of that returned promise (e.g., be replaced with the new IOU)
const originalPromise = getAsyncData(myFirstSource).then(function(firstData){
//do something with `firstData`
const newPromise = getAsyncData(mySecondSource); //a second async call!
return newPromise; //return the promise.
}); //`originalPromise` now takes on the status and data of `newPromise`
originalPromise.then(function(secondData){
//do something with `secondData`, the data downloaded from `mySecondSource`
});
All together
fetch(url)
.then(function(response) {
const dataPromise = response.json();
return dataPromise;
})
.then(function(data) {
//do something with the data!!
console.log(data);
});
Catching Errors
We can use the
.catch()
function to specify a callback that will occur if a promise is
rejected (an error occurs). This method will "catch" errors from all previous
.then
s
fetch(url)
.then(function(data) {
return response.json();
})
.then(secondCallback)
.catch(function(error) {
//called if EITHER previous callback
//has an error
//param is object representing the error itself
console.log(error.message);
})
.then(thirdCallback) //"finally"
//do even after any errors
async/await
Managing Promise callback chains can get tricky. ES 2017 introduced a new set of keywords async and await that can let you write Promise-based code synchronously.
//an `async` function is one that runs asynchronously
//(meaning it returns a promise)
async function myAsyncFunction() {
//`await` indicates that the code should "hold" until
//the asynchronous promise is fulfilled
//can only be used inside of an `async` function
const response = await fetch(url); //wait for fetch
const data = await response.json(); //wait for encode
console.log(data); //can use data ("synchronously")
}
fetch() in React
You can use fetch() in response to user interactions (e.g., button clicks) by calling the function and then assigning any data to a state variable.
function MyComponent(props){
//initialize state as empty
//make sure component doesn't error with this initial value!
const [stateData, setStateData] = useState([])
const handleClick = (event) => {
fetch(dataUri) //send AJAX request
.then((res) => res.json())
.then((data) => {
//do any data processing here...
setStateData(data); //assign data to state
//rerenders using data
})
}
return ...
}
Effect Hooks
An effect hook is to specify a side effect of rendering, such as fetching data or synchronizing a subscription.
The effect hook callback executes only once when the component first renders (after the first render)
//import the hooks used
import React, { useState, useEffect } from 'react';
function MyComponent(props) {
//specify the effect hook function
useEffect(() => {
//...do persistent work, set up subscriptions, etc
//e.g., fetch(url)
}, []) //array is the second arg to the `useEffect()` function
//It lists which variables will "rerun" the hook if they
//change
//component is rendering
return (<div>...</div>)
}
Effect Hook Cleanup
In order to "clean up" any work done in an effect hook (e.g., disconnect listeners), have the callback return a "cleanup function" to execute. Not needed for fetch()
import React, { useState, useEffect } from 'react';
function MyComponent(props) {
//specify the effect hook function
useEffect(() => {
//...do persistent work, set up subscriptions, etc
//function to run when the Component is being removed
function cleanup() {
console.log("component being removed")
}
return cleanup; //return function for React to call later
}, [])
return (<div>...</div>)
}
Action Items!
-
Complete task list for Week 9 (all items!!)
-
Problem Set 08 due Tonight!
-
Project Draft 2 due FRIDAY!!!!
- Problem Set 09 due next week (it's small)
Next time: Firebase databases!
- We'll get through as much as we can, but may need to catch up with videos over the break
info340au24-ajax
By Joel Ross
info340au24-ajax
- 45