Destructure array to display API results - reactjs

I'm trying to display results from an API but it seems like an entire section of the page isn't being returned. There are no errors present so solving this has been somewhat confusing.
My assumption is that based on the way the API has returned the data, my current setup for destructuring the data is incorrect. Here is the response I receive in the console from the API.
I want to apend these results to a page to display the name and cover of these video game titles.
Section displaying results
<Container>
<h2>
{searchedGames?.length
? `Viewing ${searchedGames.length} results:`
: 'Search for a game to begin'}
</h2>
<CardColumns>
{(searchedGames || []).map((game) => {
return (
<Card key={game.gameId} border='dark'>
{game.cover ? (
<Card.Img src={game.cover} alt={`The cover for ${game.name}`} variant='top' />
) : null}
<Card.Body>
<Card.Title>{game.name}</Card.Title>
</Card.Body>
</Card>
);
})}
</CardColumns>
</Container>
Form Handler
const handleFormSubmit = async (event) => {
event.preventDefault();
if (!searchInput) {
return false;
}
try {
const response = await getGame(searchInput);
if (!response.ok) {
throw new Error('Something went wrong...');
}
const { result } = await response.json();
const gameData = result?.map((game) => ({
gameId: game.id,
name: game.name,
cover: game.cover.url,
}));
setSearchedGames(gameData);
setSearchInput('');
} catch (err) {
console.error(err);
}
};
Upon searching, I receive the API response with the appropriate data, but the page does not display anything at all (not even the "Viewing xxx number of results" section, which I feel is a clue pointing to the searchedGames array.) I'd be happy to add any extra information or code if someone would like. I feel like there is a simple solution but I have been unable to find the method to fix this. Thank you in advance for any assistance anyone can offer!
EDIT:
There was a helpful response here recommending I check my searchedGames array and, once I did, I saw that it was empty. So the data from the API is not getting put into the "searchedGames" array. I am researching how to fix this, but if anyone responds to the question I feel this may be a helpful detail to assist. The problem is that my searchedGames array is not being filled with the API data.
EDIT 2:
export const getGame = (searchInput) => {
return fetch(`***********`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
const accessToken = data.access_token;
return fetch(`https://fathomless-river-46653.herokuapp.com/https://api.igdb.com/v4/games/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Client-ID': '********',
'Authorization': `Bearer ${accessToken}`
},
body: `
search "${searchInput}";
fields name,cover.url;`
})
});
};

Not entirely sure if there are details missing on the actual API response data format but I'll try giving an answer.
The json() method of fetch returns the response body of your HTTP request as JSON. So with your response data being the one of your first screenshot, it's an array of objects but not an object with an result attribute. However, latter is what you assume in your code:
const { result } = await response.json();
Then you proceed as if result was the response array of your first screenshot while it actually is undefined, so that gameData resolves to undefined after optional chaining.
const gameData = result?.map((game) => ({
gameId: game.id,
name: game.name,
cover: game.cover.url,
}));
Try out using the JSON response directly without destructuring. This should give you the actual array of game objects:
const result = await response.json();

Related

Accessing Data from my axios get response not working

can someone point out where am going wrong?
I have a simple API get request. this get data fine from my API
const GetMedicalPackages = async (props:IGetMedPack)=>{
const token = props.token
const data = axios({
method:"get",
responseType:"json",
url : baseURL + "medicalPackage",
headers: { "Authorization": `Bearer ${token}`}
})
await data;
return data.then((response:IMedicalPackageData| undefined)=>{
return response
})
}
This returns data like this
Data from API
Now trying to access the data with this code returns with this code always returns and undefined
useEffect(() => {
//async function fetchMyData() {
GetMedicalPackages({ token }).then((response) => {
if (response) {
const options = response.data.map((row) => {
console.log(row.MedicalInsurancePackage);
return row.MedicalInsurancePackage;
//console.log(row);
//options.push(row.MedicalInsurancePackage);
});
//setMedPackage(options);
console.log(options, response.data);
}
});
options values
I suspect it to do with the object that returned in each row but i might be wrong.
Can someone point out what is wrong please thanks
I have tried foreach and various techniques but nothing.
to get the array of strings
Just for clarity, The problem is not the axios call giving me the data. I can see data fine. But try to use the data returned is the problem. console log shows me the data. but as soon i try to map it i get undefines. Please see images
console log row give me this
activePackage: true
medicalInsurancePackage: "Standard"
medicalInsurancePackageDesc: "S Standard"
medicalInsurancePackageID:1
[[Prototype]]:Object
but console log row.medicalInsurancePackage give me undifined
Looks like your are missunderstanding how Promise are working. consider doing:
const GetMedicalPackages = async (props) => {
const token = props.token;
return await axios({
method: "get",
responseType: "json",
url : baseURL + "medicalPackage",
headers: { Authorization: `Bearer ${token}` },
});
};
You shouldn't be awaiting your variable, but should be awaiting for the result of your axios call. And then just return the response you get from it.
const data = await axios(...)
return data;

React + Fetch API. Can't set response in setState

I know this has been asked many times on this site but after going through SO questions related to this for the past 5 hours I have to throw in the towel and see if there's someone that can identify what I'm doing wrong here.
I have a fetch request in my react application that I am successfully receiving a response from but I am unable to store the response in my state. It seems to me that everything looks correct but when I attempt to store the response it simply does nothing. There are no console errors in the browser nor in my console that is running the react app. Currently the related code looks like this (Some things are slightly modified for privacy).
loginSubmission = () => {
fetch('https://genericsite.com/auth', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({"username": this.state.username, "password": this.state.password})
})
.then(res => res.json())
.then(res => {this.setState({response: res}, () => this.sendResponse())})
.catch(error => {
console.log(error);
});
}
sendResponse(){
console.log(this.state.response)
let data = {response: this.state.response};
this.props.receiveResponse(data);
}
If I do it like how I have it below though I'm able to console.log the response with no issues but from what I was reading in a similar question there's something about console.log that forces it to complete the request so it can log the result.
loginSubmission = () => {
fetch('https://genericsite.com/auth', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({"username": this.state.username, "password": this.state.password})
})
.then(res => res.json())
.then(res => {console.log('res.response'})
.catch(error => {
console.log(error);
});
}
That returns the following object:
{token: 'bigLongJumbledToken', idtoken: '', exp: 1655106045, username: 'myusername'}
exp: 1655106045
idtoken: ""
token: "bigLongJumbledToken"
username: "myusername"
[[Prototype]]: Object
And my state in this component looks like so:
this.state = {
username: '',
password: '',
response: {}
}
this.userOnChange = this.userOnChange.bind(this);
this.passOnChange = this.passOnChange.bind(this);
this.loginSubmission = this.loginSubmission.bind(this);
}
Thanks in advance for any help with this.
In this line: .then(res => {this.setState({response: res}, () => this.sendResponse())}) you are calling the setState with two arguments, it should be only one. I think that you want to store the response in the state and also execute sendResponse function with the response data but, even after you fix the call of setState the function sendResponse will not receive the updated state because react will wait to finish the current executing function that is .then() before actually update the state.
You have two ways of do what (i guess) you are trying to do:
First: use the response directly to call sendResponse
Second: use componentWillUpdate to call sendResponse after state updates
I'll give an example of the first approach cause I think is the cleanest:
.then(res => {
this.setState({response: res})
this.sendResponse(response)
})
sendResponse(res){ // expects response as a parameter
console.log(this.state.response)
// let data = {response: this.state.response}; // avoid this
this.props.receiveResponse(res);
}

useEffect calling API on page load errors

I'm working on a project that utilizes the IGDB to display information about various video game titles. I've got an API call on the search page that returns the information appropriately. Upon clicking on one of those titles from the search, I load a new page with detailed information about the game. The API call for the detailed information is the exact same as the one for the search functionality (exception being that they retrieve different fields from the DB, aside from that, exactly the same) and both return the data appropriately and in the exact same format (an array of objects.) However, I have had a lot of trouble displaying the data on the game details page.
Here is the response that I am having trouble displaying:
And, just for the sake of as much detail for you guys as possible, here is the successfully displayed response from the search page:
The way I have it set up is to run a useEffect hook on page load. The hook does run, and the data is returned, but displaying the data has been a challenge. Here is the current hook.
useEffect(() => {
async function getGameId(gameId) {
const response = await getSpecificGame(gameId);
if (!response.ok) {
throw new Error('Something went wrong...');
}
const result = response.json();
const gameData = result?.map((game) => ({
gameId: game.id,
name: game.name,
cover: game.cover,
summary: game.summary,
platforms: game.platforms,
genres: game.genres,
}));
setSelectedGame(gameData);
}
getGameId(gameId);
}, [])
With this code I receive the following error:
Uncaught (in promise) TypeError: result.map is not a function
With the error being with result.map, I'm very lost on where to go from here. I have wondered if perhaps the response.json() line is unnecessary or misplaced, but with the data returning in the exact same fashion as it does for the search page, I'm not sure what I would need to change. The error is not thrown on the response, meaning the data comes back ok, but it is thrown on the result.map() line, meaning either the result = response.json() is somehow incorrect/unnecessary, or I'm missing another line that needs to be there. This confuses me greatly though, since the other API call I make to perform the search works and is set up the same way. For extra context, I will post the properly functioning API call from the search page as well, this one is within a form handler:
const handleFormSubmit = async (event) => {
event.preventDefault();
if (!searchInput) {
return false;
}
try {
const response = await getGame(searchInput);
if (!response.ok) {
throw new Error('Something went wrong...');
}
const result = await response.json();
const gameData = result.map((game) => ({
gameId: game.id,
name: game.name,
cover: game.cover,
}));
setSearchedGames(gameData);
setSearchInput('');
} catch (err) {
console.error(err);
}
};
Here are the API calls that go with those functions.
export const getGame = (searchInput) => {
return fetch(`https://id.twitch.tv/oauth2/token?client_id=************&client_secret=****************&grant_type=client_credentials`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
const accessToken = data.access_token;
return fetch(`https://fathomless-river-46653.herokuapp.com/https://api.igdb.com/v4/games/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Client-ID': '***********',
'Authorization': `Bearer ${accessToken}`
},
body: `
search "${searchInput}";
fields name,cover.url;`
})
});
};
export const getSpecificGame = (gameId) => {
return fetch(`https://id.twitch.tv/oauth2/token?client_id=************&client_secret=**************&grant_type=client_credentials`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
const accessToken = data.access_token;
return fetch(`https://fathomless-river-46653.herokuapp.com/https://api.igdb.com/v4/games/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Client-ID': '**************',
'Authorization': `Bearer ${accessToken}`
},
body: `
fields name,summary,platforms.name,genres.name,cover.url;
where id = ${gameId};`
})
});
}
This seems to be a simple matter of appropriately changing the syntax to function with useEffect instead of a form handler, but it is proving to be quite the challenge. I've looked over probably 20 threads of similar issues, but none of which have been able to solve my problem.
I've wondered if I should go another route entirely, completely removing the useEffect hook and trying something else, but this feels like a learning opportunity on how to make use of useEffect so I'm trying my best to get this working.
I would be happy to attach any extra code to assist with an answer. I posted a very similar question yesterday and received some helpful responses, but since then have made progress and reached another roadblock point. Thank you in advance to anyone who is able to assist!
const result = response.json();
There is a small error here, you need to await it:
const result = await response.json();
In the other example you posted from a form handler, it's awaited correctly, so it works.

autocomplete suggestion for a search field by retrieving data from api

I am trying to get suggestions on a search field anytime the input field changes. However i keep running into various errors. this is what i've got so far.
export default function AutoComplete() {
const [info, setInfo] = useState([{'username': 'add a search word'},])
const [search, setSearch] = useState()
const handleChange = (e) => {
setSearch(e.target.value)
}
useEffect(() => {
const findItem = async (searchString) => {
const response = await fetch('http://localhost:8000/' + searchString, {
method: 'GET',
headers:{
'content-type': 'application/json',
},
})
let data = await response.json()
data = JSON.stringify(data)
return data
}
if (search){
var newdata = findItem(search)
setInfo(newdata)
}
else {
setInfo([{'username': 'add a search word'}])
}
}, [search])
return(
<div>
<input onChange={(e) => handleChange(e)}></input>
{info.map((suggestion) => {
return (
<div>
{suggestion.username}
</div>
)
})}
</div>)
}
The error i am running into at the moment is 'info.map is not a function' when i try to type in the input field. I decided to do my own implementation after i spent a lot of time running into this same error when i was using the material ui implementation https://mui.com/components/autocomplete/. in their implementation, they have a variable storing the data rather than calling an api and it works well. I can see that my data is being retrieved as it pops on on console.log at the end of the findItem function. Please help!
It seems that your findItem function is returning a JSON string:
data = JSON.stringify(data)
return data
Which is then set directly to the info variable:
setInfo(newdata)
This means that "info" will contain a JSON string rather than an array of JSON objects. Map doesn't work on JSON strings.
For your code to work you need to pass "setInfo" an array of objects; eg:
[
{username: "John"},
{username: "Amy"},
{username: "Peter"}
]

Properly associate Key / Value Pairs

I've been trying to figure this out but still having trouble. I have an array of objects and what I am trying to do is update the key of slug to ID and change it's value to the ID of the blog post that is returned from an API. I also want to do the same for the key featuredImage where I return data from API and then update the value in this array of objects. I don't have anyway to associate the slug to the featuredImage except for the way they appears in the original postData array of objects. If I am going to be making API calls and also manipulating the data is there a way to ensure that I don't mess up my key / value pairs here. What I've been struggling with is I can't seem to hold the order properly and my data ends up being in the wrong order in my final array of objects. Any help would be much appreciated.
const postData =[
{ slug: sample-post-1, featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/rustic20coffee20table.jpeg%3Ft=1528912781831-6.jpeg' },
{ slug: sample-post-2, featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/Amazing-Table-For-Flamboyant-Furniture-Home-Design-Ideas-With-Rustic-Furniture-Coffee-Table.jpg%3Ft=1528912781831-6.jpeg' },
{ slug: sample-post-3, featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/envoy-lookout-rooftop-11b-780x520.jpg%3Ft=1528912781831-6.jpeg' },
{ slug: sample-post-4, featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/mountain-landscape-wallpaper-29048-29765-hd-wallpapers.jpg%3Ft=1528912781831-6.jpeg'},
{ slug: sample-post-5, featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/mountain-landscape-wallpaper-29048-29765-hd-wallpapers.jpg%3Ft=1528912781831-6.jpeg' }
]
This is the function that I am trying to run but this is where I try to create a new Object based on the response the items are out of order.
const uploadImages = async (postData) => {
const newImages = [];
try {
await Promise.all(postData.map(async featuredImage => {
const option = {
method: 'POST',
url: fileAPIURL,
qs: {
access_token: ACCESS_TOKEN,
},
headers: {
'content-type': 'multipart/form-data'
},
formData: {
folder_paths: 'Blog_Media',
image: request(featuredImage)
},
json: true
}
return request(option)
.then((response) => {
response.objects.map((content) => {
return newImages.push(Object.assign({
featuredImage:content.url }));
});
})
}))
return newImages;
} catch (error) {
throw new Error('Cannot upload image to file manager, try checking
your URL');
}
}
When you run a bunch of asynchronous operations in parallel, they can complete it any order. So, when you do this:
newImages.push(...)
The newImages array is going to be in the order that your parallel async operations were completed. That will be some random order.
But, Promise.all() will keep the data in the proper order if you let it manage the returned data for you. So, inside of postData.map(), you can return an array of objects. Then Promise.all() will keep that array in the proper order relative to the other arrays. Then, at the end of the Promise.all().then(), you will have an array of arrays (all in the proper order) so all you need to do to get it into a single flat array in the proper order is to flatten that.
const uploadImages = (postData) => {
return Promise.all(postData.map(featuredImage => {
const option = {
method: 'POST',
url: fileAPIURL,
qs: {
access_token: ACCESS_TOKEN,
},
headers: {
'content-type': 'multipart/form-data'
},
formData: {
folder_paths: 'Blog_Media',
image: request(featuredImage)
},
json: true
}
return request(option).then((response) => {
return response.objects.map((content) => {
return {featuredImage: content.url};
});
});
})).then(results => {
// flatten the array of arrays into a single array
return [].concat.apply([], results);
}).catch(error => {
throw new Error('Cannot upload image to file manager, try checking your URL ');
});
}
Also, you don't seem to be actually using async/await for anything useful here to I removed that.
The one part of your code I don't understand is where you pass:
image: request(featuredImage)
as part of the formData to another request. That will put a promise into the formData since request(featuredImage) will return a promise. Is that really what you want?

Resources