Using Fetch in React to fetch data from server - javascript

I am trying to fetch data from the server(Node.js) with the following code:
componentDidMount = () => {
fetch('http://localhost:3000/numberofjobs')
.then(response => response.json())
.then(numberOfJobs => {
console.log(numberOfJobs)
})
}
That's my route in Node:
const handleNumberOfJobs = (req, res, Register) => {
Register.find({})
.then(users => {
const total = users.reduce((sum, { numberOfJobs }) => sum +
numberOfJobs, 0);
console.log(total);
})
.then(response => res.json(response))
}
One problem I'm having is the Front-end console.log is not showing up in the console, and I don't know why. In the server side, when the pages loads it does console.log the sum and everything, so it's working so I believe I am doing something wrong with React. I want to bring this info to my front-end so I could display it on the page.
Thanks a lot!!

TL;DR
There's a mistake in how you use arrow functions' implicit returns in your server side code.
The fix is to just add a return total; in the first .then(...) handler.
Details
First off, let's get it out: I agree with the comments on not neglecting error checks! (Be it fetch or anything else.)
Anyway: You use arrow functions in your .then(...) handlers. But that last statement in the first one is console.log(total). The return value of that call is undefined, which becomes the implicit return value of your arrow function. The promise then passes this on as the value of response in your second .then(...) handler. (You could verify that by adding console.log(response) in the second .then(...) handler.
The fix is to just add a return total; in the first .then(...) handler:
const handleNumberOfJobs = (req, res, Register) => {
Register
.find({})
.then(users => {
const total = users.reduce(
(sum, { numberOfJobs }) => sum +
numberOfJobs, 0
);
console.log(total); // <-- returns undefined
return total; // <-- pass on to next promise
})
.then(response => {
// console.log(response); // if you're curious
res.json(response)
})
}
}
Personal hint: Indent / lint your code for easier maintenance.

Related

Nested fetch() functions with conditional if statement in JavaScript

I have a function that runs a fetch() and at the very end of the fetch call it logs to the console.
I want to conditionally add a second fetch function before the end of the first. It also logs to the console.
I'm having trouble working out how to ensure the first log waits for the second fetch to complete and log its data before actually logging (whether or not it actually needs to run). I've been able to do this with several else statements, but I'd like to avoid the repetition.
Codepen # https://codepen.io/cfxd/pen/bGKmRZj
function alwaysRuns() {
fetch('https://api.spacexdata.com/v4/launches/latest')
.then(result => result.json())
.then(resultJson => {
const rocketId = resultJson.rocket;
const rocketName = resultJson.name;
if(rocketName.includes('Crew')) {
fetch(`https://api.spacexdata.com/v4/rockets/${rocketId}`)
.then(rocketResponse => rocketResponse.json())
.then(rocketJson => {
console.log('This fetch might not happen, depending on the conditional')
});
}
console.log('this should always log last whether the conditional causes the second fetch to run or not!');
});
}
alwaysRuns();
Use await
function alwaysRuns() {
fetch('https://api.spacexdata.com/v4/launches/latest')
.then(result => result.json())
.then(async resultJson => {
const rocketId = resultJson.rocket;
const rocketName = resultJson.name;
if(rocketName.includes('Crew')) {
await fetch(`https://api.spacexdata.com/v4/rockets/${rocketId}`)
.then(rocketResponse => rocketResponse.json())
.then(rocketJson => {
console.log('This fetch might not happen, depending on the conditional')
});
}
console.log('this should always log last whether the conditional causes the second fetch to run or not!');
});
}
alwaysRuns();
or even better
function alwaysRuns() {
fetch('https://api.spacexdata.com/v4/launches/latest')
.then(result => result.json())
.then(resultJson => {
const rocketId = resultJson.rocket;
const rocketName = resultJson.name;
if(rocketName.includes('Crew')) {
return fetch(`https://api.spacexdata.com/v4/rockets/${rocketId}`)
.then(rocketResponse => rocketResponse.json())
.then(rocketJson => {
console.log('This fetch might not happen, depending on the conditional')
});
}
}).then(() => {
console.log('this should always log last whether the conditional causes the second fetch to run or not!');
});
}
alwaysRuns();

for loop with axios, how do I know if all data are loaded

this.allMyFacilities = response[1].data
for (let i = 0; i < this.allMyFacilities.length; i++) {
axios
.get(facilityUsed(this.allMyFacilities[i].id))
.then((response) => {
this.facilitiesOnSupplyChain[this.allMyFacilities[i].id] = response.data
})
.catch((err) => {
// An error will also be thrown if you use cancel.
console.error(err)
})
}
I have such a code. I don't know how many facilities do I have. that is I loop it, But each facility has its own Axios. I want to how do I know all data from the loop are completely loaded
You can use Promise.all for this:
this.allMyFacilities = response[1].data
const promises = this.allMyFacilities.map((myFacility) => {
return axios
.get(facilityUsed(myFacility.id))
.then((response) => {
this.facilitiesOnSupplyChain[myFacility.id] = response.data
return response.data;
}).catch((err) => {
// An error will also be thrown if you use cancel.
console.error(err)
});
});
Promise.all(promises).then((allResponsesArray) => /* do sth */)
Assuming that all the asynchronous calls can be made independently, you can leverage upon the Promise.all functionality to achieve this.
this.allMyFacilities = response[1].data
await Promise.all(this.allMyFacilities.map(myFacility) => {
return axios.get(facilityUsed(myFacility.id))
.then(data => this.facilitiesOnSupplyChain[myFacility.id] = data);
});
This will allow you to execute all the axios calls in parallel, and hence decrease the overall execution time of the whole process.
PS:
I have written an await statement hance assuming that you will be wrapping this functionality in an async function.

Asserting a callback was called with Jest (without React)?

I have a suite of tests but something is just not clicking regarding callback assertions. My feeling is that the done() parameter needs to be woven in, but I'm doing it wrong.
I basically have two function structures, where the callback is either nested inside of a single then statements, or a then inside of another then:
function foo(cb) {
return fetch('foo.com')
.then(
(res)=>{
res
.json()
.then(
(data) =>cb(data)
})
.catch(err => console.error(err))
}
and
function foo(cb) {
return fetch('foo.com')
.then(()=>cb())
.catch(err => console.error(err))
}
I'm looking to assert that the callback was called in both cases.
I have tried
describe('ugh why can't I figure this out'?, () => {
it('is confusing', (done) => {
const testCb = jest.fn()
foo(testCb);
expect(testCb).toHaveBeenCalled()
done();
//failed: expected testCb to be called, but it was not called
}
})
I'm not sure how to move forward- I'm not a fan of the spaghetti on the wall approach, so I'd rather understand how to implement jest for testing async code before I just start switching in different syntax. The above code seems like it should work because the callback is part of the function execution, so if the higher order function executes, the callback would necessarily be executed, and should have been "called". Obviously this reasoning is wrong since the test isn't passing, but I'm not quite seeing the way around this.
Thanks very much for any direction/insight :).
This jest example seems to match mine- what am I missing?
describe('drinkAll', () => {
test('drinks something lemon-flavored', () => {
let drink = jest.fn();
drinkAll(drink, 'lemon');
expect(drink).toHaveBeenCalled();
});
test('does not drink something octopus-flavored', () => {
let drink = jest.fn();
drinkAll(drink, 'octopus');
expect(drink).not.toHaveBeenCalled();
});
});
You're expect is being called before the fetch comes back. Do this:
foo(testCb)
.then(_ => {
expect(testCb).toHaveBeenCalled();
done();
})
.catch(err => {
done.fail(err);
});
By chaining on to the Promise returned by foo we ensure the fetch has come back. Once you go async you have to stay that way, you can't mix sync and async code like you did in your posted code:
const testCb = jest.fn()
foo(testCb); // this can take an arbitrary amt of time
expect(testCb).toHaveBeenCalled() // but this happens immediately
done();
FWIW you can also change this
return fetch('foo.com')
.then(
(res)=>{
res
.json()
.then(
(data) =>cb(data)
})
Into this:
return fetch('foo.com')
.then((res)=> res.json())
.then(cb)
.catch((err) => ...
The extra level of nesting promises is unnecessary and makes the code hard to read.

Extracting array elements from JSON (javascript)

I'm trying to manipulate JSON data received from an API url (this is my first time handling this type of work)
The following function returns a promise of a 20 element array:
const articles = () => {
return fetch(url)
.then(res => res.json())
.then(post => post.articles);
};
Console view:
Now, I'd like to extract the elements from the array - I tried something like:
articles()[0].name
but this doesn't work and I'm not sure of an alternative way to go about this? Appreciate your help. Thanks
Your articles fucntion returns a promise. You have to consume the promise (more on MDN):
articles().then(articleArray => {
console.log(articleArray);
});
or within an async function:
const articleArray = await articles();
console.log(articleArray);
Side note: Your fetch code is missing a check for HTTP success (HTTP failure isn't a rejection). You're by far not the only person who misses out this check, so much so that I've written a post on my anemic blog about it. With the check:
const articles = () => {
return fetch(url)
.then(res => {
if (!res.ok) {
throw new Error("HTTP error " + res.status);
}
return res.json();
})
.then(post => post.articles);
};

Dynamically update html content plain JS

So I have a script bound in my index.html, which literally has to fetch an array of jsons from an api, then dynamically update my page when get response.
But as a result I'm getting an as the only update of my page.
here is my script
fetch(url)
.then((resp) => resp.json())
.then((resp) => {
resp.results.forEach(item => {
fetch(item.url)
.then((response)=> response.json())
.then((response) => pokeArr.push(response))
.catch(console.log)
})
}).catch(console.log)
const filler = () => {
if(!pokeArr) return 0
pokeArr.map((i,j)=>{
return `<tr>${i.name}</tr>`
})
}
const pokeindex = () => {
document.getElementById('a').appendChild(document.createElement(filler()))
}
pokeindex()
When I'm consoling it, I can see in the console all the responses I get, so I'm at least doing the fetching part right.
There are a number of issues here:
You have parberakan in one place but pokeArr in another; in a comment you've suggested they're meant to be the same thing.
You don't declare parberakan/pokeArr anywhere in the quoted code.
Nothing in the quoted code is waiting for the various fetch calls to complete (not even the first, and esp. not the series of them in one of its then handlers).
The fetch calls aren't checking .ok; see the post on my anemic little blog.
document.createElement doesn't accept HTML strings. It accepts the tag name of the single element to create (e.g., document.createElement("div")).
filler doesn't return the result of map.
Passing console.log directly to catch isn't 100% reliable.
At a rough guess, I suspect you want to do something like this (see comments), but I do recommend stepping back from your current task and working through some basic tutorials:
// Create these functions first
const filler = (pokeArray) => { // Accept the array as a parameter
if(!pokeArray) return null; // Recommend null instead of 0 if the other return is an array
return pokeArray.map((i,j)=>{ // Return the result of `map`
return `<tr>${i.name}</tr>`;
});
};
const pokeindex = (pokeArray) => { // Accept the array as a parameter
// I'm assuming the element with id="a" is a `table` or `tbody`
document.getElementById('a').insertAdjacentHTML( // This accepts HTML
"beforeend",
fillter(pokeArray)
);
}
fetch(url)
.then((resp) => {
// Check .ok
if (!resp.ok) {
throw new Error("HTTP error " + resp.status);
}
return resp;
})
.then((resp) => resp.json())
.then((resp) => Promise.all(resp.results.map(item => { // Wait for all these to complete;
// result is an array of the
// resolution values
return fetch(item.url)
.then((response) => {
// Check .ok
if (!response.ok) {
throw new Error("HTTP error " + resp.status);
}
return response;
})
.then((response) => response.json());
})))
.then((pokeArray) => {
// Now we have the array and can do something
pokeindex(pokeArray);
})
.catch(error => { console.log(error); }); // Passing `console.log` directly isn't reliable
I may not have caught every last little thing, though. This is just to help get you pointed the right way.

Categories