Issues when trying to retrieve API data using React - javascript

For some reason I'm unable to access the "question" key within the results array. Could someone help please? This is just a side project I've started and have been tearing my hair out for hours trying to get this first basic part to work. I've only been using react and API's for about a week now and I'm super inexperienced, so any help would be appreciated.
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component {
constructor() {
super()
this.state = {
questionList: []
}
}
componentDidMount() {
fetch('https://opentdb.com/api.php?amount=10')
.then(resp => resp.json())
.then(resp => this.setState({ questionList: resp }))
}
render() {
return (
<div>{this.state.questionList.results[0].question}</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
and here's the API data -
{
"response_code": 0,
"results": [
{
"category": "Entertainment: Film",
"type": "multiple",
"difficulty": "medium",
"question": "Which of the following James Bond villains is not affiliated with the SPECTRE organization?",
"correct_answer": "Auric Goldfinger",
"incorrect_answers": [
"Dr. Julius No",
"Rosa Klebb",
"Emilio Largo"
]
},
{
"category": "Geography",
"type": "multiple",
"difficulty": "hard",
"question": "The mountainous Khyber Pass connects which of the two following countries?",
"correct_answer": "Afghanistan and Pakistan",
"incorrect_answers": [
"India and Nepal",
"Pakistan and India",
"Tajikistan and Kyrgyzstan"
]
},
{
"category": "Entertainment: Video Games",
"type": "multiple",
"difficulty": "medium",
"question": "In Team Fortress 2, which class wields a shotgun?",
"correct_answer": "Everyone Listed",
"incorrect_answers": [
"Heavy",
"Soldier",
"Engineer"
]
},
{
"category": "Entertainment: Video Games",
"type": "multiple",
"difficulty": "easy",
"question": "Who is the leader of Team Mystic in Pokémon Go?",
"correct_answer": "Blanche",
"incorrect_answers": [
"Candela",
"Spark",
"Willow"
]
},
{
"category": "Science & Nature",
"type": "multiple",
"difficulty": "easy",
"question": "The medical term for the belly button is which of the following?",
"correct_answer": "Umbilicus",
"incorrect_answers": [
"Nevus",
"Nares",
"Paxillus"
]
},
{
"category": "Entertainment: Cartoon & Animations",
"type": "multiple",
"difficulty": "easy",
"question": "What is lost in Hawaiian and is also the name of a little girl in a 2002 film which features a alien named "Stitch"?",
"correct_answer": "Lilo",
"incorrect_answers": [
"Lolo",
"Lucy",
"Lulu"
]
},
{
"category": "Entertainment: Cartoon & Animations",
"type": "multiple",
"difficulty": "hard",
"question": "In "Gravity Falls", what does Quentin Trembley do when he is driven out from the White House?",
"correct_answer": "Eat a salamander and jump out the window.",
"incorrect_answers": [
"Leave in peace.",
"Jump out the window.",
"Release 1,000 captive salamanders into the white house."
]
},
{
"category": "Entertainment: Television",
"type": "multiple",
"difficulty": "hard",
"question": "Who was the winner of "Big Brother" Season 10?",
"correct_answer": "Dan Gheesling",
"incorrect_answers": [
"Bryce Kranyik",
"Ryan Sutfin",
"Chris Mundorf"
]
},
{
"category": "General Knowledge",
"type": "multiple",
"difficulty": "easy",
"question": "Terry Gilliam was an animator that worked with which British comedy group?",
"correct_answer": "Monty Python",
"incorrect_answers": [
"The Goodies‎",
"The League of Gentlemen‎",
"The Penny Dreadfuls"
]
},
{
"category": "Entertainment: Video Games",
"type": "multiple",
"difficulty": "easy",
"question": "In Counter-Strike: Global Offensive, what's the rarity of discontinued skins called?",
"correct_answer": "Contraband",
"incorrect_answers": [
"Discontinued",
"Diminshed",
"Limited"
]
}
]
}
Thanks!

This will surely fail, since you are calling an asynchronous action, but your render relies on that explicitly:
{this.state.questionList.results[0].question}
You are probably receiving Cannot read property 'question' of undefined
or
Cannot read property '0' of undefined
The most basic solution would be to check if the questionList actually has some data inside.
Note: questionList in an array, not an object.
render() {
if (this.state.questionList.length === 0) {
return null;
}
return (
<div>{this.state.questionList[0].results[0].question}</div>
);
}
However the best, but the longest solution would be check step-by-step that questionList has a correct structure:
{this.state.questionList[0]
&& this.state.questionList.results
&& this.state.questionList.results[0]
&& this.state.questionList.results[0].question}

As other's have noted, your API call will take a period of time, and your App will render before it is done, so your data isn't there yet when it goes to render. You're basically trying to get data that doesn't exist yet.
I like to have a loading flag on my state to show loading stuff, as well an an error flag for any errors that come in.
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component {
constructor() {
super()
this.state = {
questionList: {
isLoading: true,
error: null,
data: null,
}
}
}
componentDidMount() {
fetch('https://opentdb.com/api.php?amount=10')
.then(resp => resp.json())
.then(resp => this.setState(
{questionList:
{data: resp, isLoading: false, error: null}
}))
.catch(e => this.setState({questionList: {data: null, error: e, data: null}}))
}
render() {
if (this.state.questionList.isLoading)
return <Loading />
if (this.state.questionList.error)
return <Error error={this.state.questionList.error} />
return (
<div>{this.state.questionList.data.results[0].question}</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
You can and should be a little smarter with your checks, and data fixing, etc, etc... but that is the gist of how I deal with it.
I also recommend wrapping up your API calls in a module. So you could say api.questions.getList().then(...) instead of having the fetch in your components, and any data conversions, or other ugly stuff can be put in those modules. It isn't really related to your question (and would probably be overkill for this anyways), but since you seem to new to react I'll throw that out there for you.

use
console.log in render()
and you will see that your state is empty, because you call async function.
There is:
Api call - it can take 1 sec, 10 sec, 20 sec etc...
call render() - your state is empty because you need to wait for API response
you will get error...
You can add some code, eg:
render() {
if (this.state.questionList.length === 0) {
return '<div>Loading...</div>';
}
return (
<div>{this.state.questionList.results[0].question}</div>
)
}

I am not sure that your results are back to the component by the time it renders. I think you need to run a check to see if the results are there. Can you try something like this and we can go from here?
{this.state.questionList.results.length > 1 && this.state.questionList.results[0].question}

Related

React rerendersa all inputs on one input state change, killing all performance and making experience laggy

I'm developing some selected products manager and encountered a problem i can't think of solution by myself. I have main state with all the data from api. It looks like array of
{
"img_url": "http://localhost/wp-content/uploads/2022/12/g",
"productTitle": "Test",
"productId": "17",
"inputs": [
{
"inputTitle": "Text",
"inputType": "text",
"inputValue": "Test",
"id": "133"
},
{
"inputTitle": "SELECT",
"inputType": "select",
"inputValues": [
"1",
"2",
"3",
"4"
],
"inputValue": "3",
"id": "134"
}
],
"inputsValues": [
{
"inputValue": "Test"
},
{
"inputValue": "3"
}
],
"orderTitle": "MLF-123",
"unique_id": "1b4b0ad6-b796-4608-bc41-5d6"
}
And inputs have onchange that edits the actual values
onChange={(event) => {
const selectedProductsCopy = [...selectedProducts];
selectedProductsCopy[selectedProductIndex].inputsValues[
inputIndex
].inputValue = event.target.value;
setSelectedProducts([...selectedProductsCopy]);
}}
The problem i'm focusing is website performance, on onchange every render (there can be up to 50) rerenders, and i can't think of solution because i'm still not very experienced with render optimization. All i can add to this is the profiler with rerender data.
If you can help me and need any additional data please comment, ill answer shortly.

How to implement hyperlinks in JSON & D3 plugin?

My goal here is to have the childless nodes contain hyperlinks. This is the D3 plugin I'm basing things off of: https://github.com/deltoss/d3-mitch-tree
Image Example
I'm newer to JS and JSON so I'm having difficulties on figuring out how to proceed, especially since there's little to refer to in regard to hyperlinks & JSON. If there's a better way to go about this, I'm certainly open to new ideas.
Thank you in advance
<script src="https://cdn.jsdelivr.net/gh/deltoss/d3-mitch-tree#1.0.2/dist/js/d3-mitch-tree.min.js"></script>
<script>
var data = {
"id": 1,
"name": "Animals",
"type": "Root",
"description": "A living that feeds on organic matter",
"children": [
{
"id": 6,
"name": "Herbivores",
"type": "Type",
"description": "Diet consists solely of plant matter",
"children": [
{
"id": 7,
"name": "Angus Cattle",
"type": "Organism",
"description": "Scottish breed of black cattle",
"children": []
},
{
"id": 8,
"name": "Barb Horse",
"type": "Organism",
"description": "A breed of Northern African horses with high stamina and hardiness. Their generally hot temperament makes it harder to tame.",
"children": []
}
]
}
]
};
var treePlugin = new d3.mitchTree.boxedTree()
.setData(data)
.setElement(document.getElementById("visualisation"))
.setMinScale(0.5)
.setAllowZoom(false)
.setIdAccessor(function(data) {
return data.id;
})
.setChildrenAccessor(function(data) {
return data.children;
})
.setBodyDisplayTextAccessor(function(data) {
return data.description;
})
.setTitleDisplayTextAccessor(function(data) {
return data.name;
})
.initialize();
</script>
Chain this method before .initialize():
.on("nodeClick", function(event, index, arr) {
if (!event.data.children.length) {
console.log('you clicked a child-less item', event.data);
}
})
Inside the condition, event.data is the clicked childless item. Feel free to place URLs inside those objects and use those URLs to navigate away.
Taken from the repo's /examples folder, where I found one named Advanced example with Node Click Event.
According to the comment on the same page, you can also achieve this using the options syntax:
/* const tree = [
place your data here...
]; // */
new d3.mitchTree.boxedTree({
events: {
nodeClick({ data }) {
if (!data.children.length) {
console.log('You clicked a childless item', data);
}
}
}
})
.setData(tree)
// ...rest of chain
.initialize();

Filter JSON Array in ReactJS component

I am new to the community and English is not my native language (I speak Spanish), but I will try to ask my question as clearly as possible.
I'm learning React and I am working on a component that imports a JSON file containing an array. I'm trying to figure out how to filter the array to be able to show information according to the url patch.
File React:
import React from 'react';
import Lista from '../../Arrays/Lista';
const Card = ({ match }) => {
const product = Lista.filter( p => p.id === parseInt(match.params.id))
return (
<div>
<h1> {product.title} </h1>
<p>{product.description}</p>
<img src={product.image} alt="imagen"/>
</div>
)
}
export default Card;
File JSON:
[
{
"id": 0,
"title": "FunkoPop Woody",
"description" : "FunkoPop Woody Edicion Limitada",
"price": "11.900",
"image": "https://ripleycl.imgix.net/https%3A%2F%2Fs3.amazonaws.com%2Fimagenes-sellers-mercado-ripley%2FImagenes-MIRAKL%2FMPM00002571116%2FMPM00002571116-1-F.jpg?w=750&h=555&ch=Width&auto=format&cs=strip&bg=FFFFFF&q=60&trimcolor=FFFFFF&trim=color&fit=fillmax&ixlib=js-1.1.0&s=95653b7ade9c0f60337f9b396fc0b544"
},
{
"id": 1,
"title": "FunkoPop Stitch",
"description" : "FunkoPop Stitch Regular Edition",
"price": "9.900",
"image": "https://www.tiendawabro.com.ar/media/catalog/product/cache/1/image/1800x/040ec09b1e35df139433887a97daa66f/2/3/2353.jpg"
},
{
"id": 2,
"title": "FunkoPop Jinx",
"description": "FUnkoPop League of Legends",
"price": "10.900",
"image": "https://www.metalgame.cl/wp-content/uploads/2018/01/889698103053.jpg"
}
]

Is it possible to get a list of all items in an array of objects that match items in another array?

I'm working on a React project that'll allow me to search through a list of games to help me decide what to play based on what I'm in the mood for, currently I can add games to a JSON file but I'm really struggling with the searching part.
Right now, to add a new game, you'll enter the title, genre(s) and a description of the game. The genre field is a ReduxForm FieldArray object, and I think that's what's giving me the trouble. Here's my current JSON file
{
"games": [
{
"name": "Rainbow Six: Siege",
"genres": [
{
"genre": "tactical"
},
{
"genre": "shooter"
}
],
"description": "tactical team based shooter",
"id": 1
},
{
"name": "Resident Evil 2",
"genres": [
{
"genre": "horror"
},
{
"genre": "survival"
},
{
"genre": "shooter"
}
],
"description": "classic resident evil 2 remake in 2019",
"id": 2
},
{
"name": "Rocket League",
"genres": [
{
"genre": "cars"
},
{
"genre": "competition"
},
{
"genre": "one more game"
}
],
"description": "soccar!",
"id": 3
}
]
}
This is the dummy data I'm using to search:
const searchedGenres = 'horror, shooter';
const searchedList = searchedGenres.split(', ');
let foundGame = [];
Once I get the search working with this data, the plan is to allow me to just type in data on the frontend in one textbox, so "horror, shooter" would be my search term. The result from this search should only return Resident Evil 2, however I'm also receiving Rainbow Six: Siege as a result, even though it's missing one of my requested genres.
searchedList.forEach(searchedGenre => {
this.props.games.map(game => {
if (
game.genres.find(
({ genre }) =>
genre.toLowerCase() ===
searchedGenre.toLowerCase().trim()
) !== undefined
) {
foundGames.push(game);
}
});
});
I understand why I'm getting both Rainbow Six and Resident Evil as a result, because I'm not actually checking that both genres are in the games genres when I add the game to the foundGames array, but I'm completely lost on how I'd go about making sure all of the genres are in a game before I add it.
This would be a bit easier if your genres was a simple array of strings rather than objects, but still you can check pretty succinctly by leveraging some() and every() within filter() (btw filter() is a better choice than map() + push() here)
let games = [{"name": "Rainbow Six: Siege","genres": [{"genre": "tactical"},{"genre": "shooter"}],"description": "tactical team based shooter","id": 1},{"name": "Resident Evil 2","genres": [{"genre": "horror"},{"genre": "survival"},{"genre": "shooter"}],"description": "classic resident evil 2 remake in 2019","id": 2},{"name": "Rocket League","genres": [{"genre": "cars"},{"genre": "competition"},{"genre": "one more game"}],"description": "soccar!","id": 3}]
const searchedGenres = 'horror, shooter';
const searchedList = searchedGenres.split(', ');
let foundGame = games.filter(game => searchedList.every(searchItem => game.genres.some(g => g.genre == searchItem) ))
console.log(foundGame)
The filter condition basically says you want every game in searchedList to match at least one genre in the game. This will make it only return games that match every genre.

How to add specified images for buttons clicked in java script node/html?(I am new)

I am having troubles in adding images to my adventure game.
I am doing this for school. I got the information down, but I am stuck in trying to add specific images so they can show up once I press the button to progress the story.
Sorry if how I describe my problem comes across confusing...
The code shows up fine. All I want to do is add some images to my game.
Here's a link to the code, and here's the script within the HTML. I am not sure if I am doing it right.
EDIT:: (UPDATED LINK TO JSFIDDLE)
fiddle
var db = [
{
"question": "<br><b> From where he stands, there is a door way. To the left, there is a mirror.</b>",
"answers": [{
"title": "Go through the doorway.",
"response": 1
},
{
"title": "Look into the mirror.",
"response": 2
}
]
}, {
"question": "<b> As he was approaching the doorway, he asked himself: <i> Why Would I want to do that?</i></b>",
"answers": [
{
"title": "Go back.",
"response": 0
}
]
},
{
"question": "<b>The man looks at himself and sees that he appear to be a blue-haired man. For some reason, the man feels unsettled with the reflection that's being projected.</b>",
"answers": [{
"title": "Check pockets",
"response": 3
}]
},
{
"question": "<b>In his pockets there's a wallet, small notebook and a watch. The Watch isn't working, so he puts the watch away. Both of the objects has the name <i> Dimitri Zimmerman </i> written on them. The man now remembers his name.</b>",
"answers": [{
"title": "Check wallet.",
"response": 4
},
{
"title": "Check the note-book.",
"response": 6
}
]
},
{
"question": "<b> Dimitri opened the wallet and quickly figured out as to why he felt this disembodied feeling when faced with the refection. Dimitri's appearance changed entirely. The ID photo presents a more mundane appearance than the demonic appearance that now presents him. Dimitri also finds out he's a Detective by examining his ID.</b>",
"answers": [{
"title": "Check the note-book.",
"response": 5
} ]
},
{
"question": "<b> While putting the wallet back in his pocket, Dimitri began flipping through the pages.</b>",
"answers": [
{
"title": "...How odd",
"response": 6
}
]
},
{
"question": "<b>There appears to be some sort of writing within the pages that Dimitri can't comprehend. Briskly he raffled through the pages to find anything legible.</b>",
"answers": [{
"title": "Keep Looking",
"response": 7
}]
},
{
"question": "<b> Dimitri continue to flip through the illegable pages. On the last page you are able read what's written.</b>",
"answers": [
{
"title": "Read.",
"response": 8
}]
},
{
"question": "<b> Crudely written in red-ink, it read:<br> <i> I have gone beyond than any mortal should, I might lose my mind or something once making contact with the Black Lodge... Remember I am looking for ----- R---. I must not forget why I am here, so I have this notebook to remember if I were to become lost. I must apprehend that murder.</i> Where the name should be displayed, it's the same incoherent scribbles on the pages before.</b>",
"answers": [
{
"title": "Put the note-book away.",
"response": 9
}]
},
{
"question": " <b> After putting the notebook away, Dimitri stepped away from the mirror and looked over to the doorway, still hazed with the details of why he is here. All that Dimitri knows that he's a Detective who's after a murder.</b>",
"answers": [
{
"title": "Leave the room.",
"response": 10
}]
},
{
"question": "<b>To be Continued...</b>",
}];
If you want to add different image for different question, you can modify the json as below.
Can add the individual image path to each json, or can ignore if no need of image for any question.
if(db[currentLocation].img){
document.getElementById("img").src = db[currentLocation].img
}else{
document.getElementById("img").src = ''
}
overall code
function pictureChange() {
document.getElementById("theImage").src = "http://31.media.tumblr.com/fca646cd8fe87906e605ad7e8d039903/tumblr_mmoz4fWT6U1soh1p8o1_500.png";
}
var db = [
{
"question": "<br><b> From where he stands, there is a door way. To the left, there is a mirror.</b>",
"answers": [{
"title": "Go through the doorway.",
"response": 1
},
{
"title": "Look into the mirror.",
"response": 2
},
]
}, {
"question": "<b> As he was approaching the doorway, he asked himself: <i> Why Would I want to do that?</i></b>",
"img": "http://31.media.tumblr.com/fca646cd8fe87906e605ad7e8d039903/tumblr_mmoz4fWT6U1soh1p8o1_500.png",
"answers": [
{
"title": "Go back.",
"response": 0
},
]
},
{
"question": "<b>The man looks at himself and sees that he appear to be a blue-haired man. For some reason the man feels unsettled with the reflection that's being projected.</b>",
"img": "http://31.media.tumblr.com/fca646cd8fe87906e605ad7e8d039903/tumblr_mmoz4fWT6U1soh1p8o1_500.png",
"answers": [{
"title": "Check pockets",
"response": 3
}]
},
{
"question": "<b>In his pockets there's a wallet, small note-book and a watch. The Watch isn't working, so he puts the watch away. Both of the objects has the name <i> Dimitri Zimmerman </i> written on them. The man now remembers his name.</b>",
"answers": [{
"title": "Check wallet.",
"response": 4
},
{
"title": "Check the note-book.",
"response": 6
},
]
},
{
"question": "<b> Dimitri opened the wallet and quickly figured out as to why he felt this disembodied feeling when faced with the refection. Dimitri's appearance changed entirely. The ID photo presents a more mundane appearance than the demonic appearance that now presents him. Dimitri also finds out he's a Detective by examining his ID.</b>",
"answers": [{
"title": "Check the note-book.",
"response": 5
},]
},
{
"question": "<b> While putting the wallet back in his pocket, Dimitri began flipping through the pages.</b>",
"answers": [
{
"title": "...How odd",
"response": 6
},
]
},
{
"question": "<b>There appears to be some sort of writing with in the pages that Dimitri can't comprehend. Briskly he raffled through the pages to find anything legable.</b>",
"answers": [{
"title": "Keep Looking",
"response": 7
},]
},
{
"question": "<b> Dimitri continue to flip through the illegable pages. On the last page you are able read what's written.</b>",
"answers": [
{
"title": "Read.",
"response": 8
},
]
},
{
"question": "<b> Crudely written in red-ink, it read:<br> <i> I have gone beyond than any mortal should, I might lose my mind or something once making contact with the Black Lodge... Remember I am looking for ----- R---. I must not forget why I am here, so I have this note-book to remember if I were to become lost. I must apprehend that murder.</i> Where the name should be displayed, it's the same incohearent scribbles on the pages before.</b>",
"answers": [
{
"title": "Put the note-book away.",
"response": 9
},
]
},
{
"question": " <b> After putting the note book away, Dimitri stepped away from the mirror and looked over to the doorway, still hazed with the details of why he is here. All that Dimitri knows that he's a Detective who's after a murder.</b>",
"img": "http://31.media.tumblr.com/fca646cd8fe87906e605ad7e8d039903/tumblr_mmoz4fWT6U1soh1p8o1_500.png",
"answers": [
{
"title": "Leave the room.",
"response": 10
},
]
},
{
"question": "<b>To be Continued...</b>",
},
];
var currentLocation = 0;
window.printCurrentLocation = function () {
document.getElementById("question").innerHTML = db[currentLocation].question;
debugger;
if (db[currentLocation].img) {
document.getElementById("img").src = db[currentLocation].img
} else {
document.getElementById("img").src = ''
}
var answers = "";
for (var i = 0, l = db[currentLocation].answers.length; i < l; i++) {
answers += "<p><button onclick='setLocation(" + db[currentLocation].answers[i].response + ")'>" + db[currentLocation].answers[i].title + "</button></p>";
}
document.getElementById("answers").innerHTML = answers;
}
window.setLocation = function (num) {
currentLocation = num;
window.printCurrentLocation();
}
window.printCurrentLocation();

Categories

Resources