I was going through a refactoring phase in my last project and decided to tidy up my fetch requests. I have multiple functions that not only look similar, but they all are about 49 lines long.
handleClick = () => {
// fetches arrivalTimes
fetch(`https://subway-times-api.herokuapp.com/stops/${this.props.stop.id}`)
.then(r => r.json())
.then(data => {
// fetch here
fetch(`https://subway-times-api.herokuapp.com/lines/${data.lines[0].id}`)
.then(r => r.json())
.then((line) => {
// diggs through feed to find the arrays with the arrival times
let feed = line.feed.filter( obj => Object.keys(obj).includes("trip_update"))
let includesStopTimeUpdate = feed.filter(obj => Object.keys(obj.trip_update).includes("stop_time_update"))
let stopTimeUpdateArrays = includesStopTimeUpdate.map(obj => obj.trip_update.stop_time_update)
let stopTimeArrays = stopTimeUpdateArrays.map(obj => obj.map(obj2 => obj2))
let trainObjs = []
// adds the objects with train arrival times and stop ids to "state"
stopTimeArrays.map(obj => obj.map(obj2 => trainObjs.push(obj2)))
let arrivalTimes = trainObjs.filter(obj => obj.stop_id.includes(this.props.stop.stop_id + this.props.direction.direction))
let trainArrivalObjs = arrivalTimes.map(obj => {
let trainTime = new Date( parseInt(obj.arrival.time) *1000);
let timeNow = new Date()
// setting hours and mins
let trainHour = trainTime.getHours() > 12? trainTime.getHours() - 12 : trainTime.getHours()
let trainMin = trainTime.getMinutes()
let currentHour = timeNow.getHours() > 12? timeNow.getHours() - 12 : timeNow.getHours()
let currentMin = timeNow.getMinutes()
// if trainHour is > current hour add 60 mins to trainMin
if (trainHour > currentHour) {
trainMin += 60
}
// take hour and min of train time and subtract each from the current time, if result is negative return 0
return trainMin - currentMin
})
// if train is due or has past remove
const arrivals = trainArrivalObjs.filter(time => time >= 0)
this.setState({
renderStopInfo: !this.state.renderStopInfo,
arrivals: arrivals
})
})
})
}
This function throws away the whole DRY rule, and it's time to fix that. I've decided to throw all the logic here into my backend, so when I fetch for a stop, I get back all uptown and downtown arrival times converted to hours and minutes. To further refactor, I've decided to use async and await to handle my fetch.
Async
Async is a keyword before a function, and when called, the function returns a promise.
async function hello() {
return "Hello, World!"
}
console.log(hello()) => Promise {<resolved>: "Hello, World!"}
Await
The MDN docs state "This [await function] can be put in front of any async promise-based function to pause your code on that line until the promise fulfills, then return the resulting value. In the meantime, other code that may be waiting for a chance to execute gets to do so."
So instead of writing my event handlers like so:
fetch(API)
.then(response => responce.json())
.then(data => this.setState({apiObject: data})
I can write the same thing with async/await
async function fetchFromApi(api) {
let response = await fetch(api)
let apiObject = response.json()
return apiobject
}
fetchFromApi(API).then((apiObject)) => {
this.setState({
apiObject: responseObject
})
})
In my opinion, the later is a lot easier to read. Also, because It's reusable, I can just call on fetchFromApi
with any URL.
Top comments (3)
Great post Lori!
I've found that for doing routine fetch calls like this, its even easier to do
This way you don't add unnecessary variables or add to the call stack, but still keep your fetch call down to one line.
Nice! Thanks for the tip, Taylor!
Nice! Thanks for sharing Lori