In this application, I am trying to create a quiz application.
import React, { useState, useEffect, Component } from 'react';
const axios = require('axios').default;
const PlayQuiz = () => {
// declaring all the state here
const [questionsArray, setQuestionsArray] = useState([]);
// Using effects here
useEffect(() => {
axios({
method: 'get',
url: 'https://opentdb.com/api.php?amount=10',
}).then(res => {console.log(Object.values(res.data)[1]); setQuestionsArray(Object.values(res.data)[1])})
.catch(err => console.error(err))
}, []);
useEffect(() => {console.log(questionsArray)}, [questionsArray]);
// Returning html markup here
return (<>
<div className = 'questions-container'>
{/* {questionsArray.map(questionObject => <h1>{questionObject.question}</h1>)} */}
<h1>{questionsArray[0].question}</h1>
</div>
</>)
}
export default PlayQuiz;
(btw all the console logs that this code contains is just for me to visualise of what exactly is going on)
In the following code, I use axiosto fetch data from the API and then resolve the data in my questionsArray. Then I want to print a heading tag into my dom which contains the first element inside my array, i.e. the object and get that object's question property which contains the actual question. But when I do this: <h1>{questionsArray[0].questions}</h1>, it throws an error, saying cannot read property questions of undefined.
Just in case, if anyone of you want to see the object that I get from the API:
And from this object, I get the results object value from the data key in the object, and set it to questionsArray.
If anyone of you wants to see what gets stored inside my questionsArray:
How can I fix this error?
You can try optional chaining ?.
<h1>{questionsArray[0]?.question}</h1>
Check this out:
https://codesandbox.io/s/strange-lovelace-06u3x?file=/src/App.js
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
When React is rendering your data, axios is not completely fetced your data and your questionsArray is either undefined or empty. Just check if axios is finished fetching data.
return (<>
<div className = 'questions-container'>
{/* {questionsArray && questionsArray.map(questionObject => <h1>{questionObject.question}</h1>)} */}
<h1>{questionsArray[0].question}</h1>
</div>
</>)
This will ensure your data is defined or not empty
Related
I am trying to get the images data from flickr. I am getting the response but not able to display the image.I have tried using useEffect to fetch the api and useState variable to store images array. Still it was getting called two times.
const DisplayImages = () => {
let photos = [];
fetch(
` https://www.flickr.com/services/rest/?method=flickr.galleries.getPhotos&api_key=***********************&gallery_id=************&format=json&nojsoncallback=1`
)
.then((response) => response.json())
.then((res) => {
const images = res;
console.log(res);
photos = images.photos.photo;
});
return (
<>
<div className="imagescontainer">
{photos.map((photo) => {
console.log(photo);
let url = `https://farm${photo.farm}.staticflickr.com/${photo.server}/${photo.id}_${photo.secret}`;
<>
{/* <img src={require(url)} alt="img" /> */}
</>
})}
</div>
</>
);
};
I expected the images to be rendered.
It's getting called twice because React is probably configured to be in strict mode in development, which causes hooks to be called twice.
Second, you're using a locally-scoped variable photos to hold state, when you need to use useState instance, otherwise React won't know when the state changes and re-render the component.
Third, it looks like you're setting photos, which is initialized with an empty array, to a single instance of a photo.
I have created a hook in a component as below.
useEffect(() => {
axios
.get("http://127.0.0.1:5000/v1/matches")
.then((response) => {
getStatusCode(response.data.code);
console.log("responseCode",responseCode);
getMatchdata(response.data.result);
setInfo(<MatchData responseCode={responseCode} matchdata={matchdata} />);
})
.catch((error) => console.log(error));
},[]);
This is a state function used in the above effect
const [info, setInfo] = useState();
I expected the above useEffect should return me some data in the below block
<div> {info} </div>
but it is showing wrong data, whereas I have created another function to trigger on Refresh button as
function refresh() {
setInfo(<MatchData responseCode={responseCode} matchdata={matchdata} />);
}
this function is returning me correct data. I want to create a functionality that will dynamically update the div element with change in state of {info}, by default when the page is loaded first, it should fetch data from the endpoint used here only. I'm new to React. Where I'm going wrong and how do I achieve it?
I don't want to say this is wrong, but this seems like an atypical approach from what I've seen in the wild. Specifically I am talking about storing a JS/JSX or TS/TSX element in a state object. I have more commonly seen a value stored in that type of variable and that value changing when necessary via the set dispatch function. Then the state object is passed to the component who needs it to do something. In react, when the value of that state object changes, it will cause the component who uses it to re-render. If I were coding this, this is what my code would look like.
const [info, setInfo] = useState();
const getData = () => {
axios
.get("http://127.0.0.1:5000/v1/matches")
.then((response) => {
setInfo(response.json())
})
.catch((error) => console.log(error));
}
const divComponent = ({info}) => (
<div>
<p>{info.data.code}</p>
<p>{info.data.result}</p>
</div>
)
const refreshButton = () => (
<button onClick(()=>getData())>Refresh</button>
)
Unless you only specifically want something to happen once at component mount, you would not use useEffect() like you did in your code. If the decision to refresh were coming from an external object with state instead of the refresh button, you could add that object whose state changes to the dependency array of the useEffect function. This would cause the refresh to run any time that object's state value changes. In the code above, getData() (which might need to be async) will only run when called. Then you have a component called divComponent which is expecting info to have value. When rendering this component you would want a null check like I coded below. Finally the refreshButton component will call getData() when it is clicked.
Then in your code that renders this, I would have something like this:
<>
{info ? <divComponent info={info} /> : <p>There is no info</p>}
<refreshButton />
</>
The code above will check if the state object info has value, and if it does it will render the divComponent with your data values. If it does not, instead it will show the p tag explaining that there is no data. Either way it will render the refreshButton, which would run the getData() function again when clicked.
** EDIT **
Based on your comment, here is another approach so you can have a value on page load and update when necessary:
import {useState, useEffect} from "react";
const [info, setInfo] = useState();
const getData = () => {
axios
.get("http://127.0.0.1:5000/v1/matches")
.then((response) => {
setInfo(response.json())
})
.catch((error) => console.log(error));
}
useEffect(()=> {
getData();
}, [])
const divComponent = ({info}) => (
<div>
<p>{info.data.code}</p>
<p>{info.data.result}</p>
</div>
)
const refreshButton = () => (
<button onClick(()=>getData())>Refresh</button>
)
export const Page = () => (
<>
{info ? <divComponent info={info} /> : <p>There is no info</p>}
<refreshButton />
</>
);
your method is quite complex. I believe you need to add your MatchData Component Inside the div in this way.Also Don't Need To Call State Method setInfo() in useEffect hook.Only responseCode and matchdata Needed that is already adjusted by you in useEffect Hook.
Preface: I'm fairly new to React (Coming over from Angular). I know things a similar but different.
I have referenced the following SO threads to no avail in my situation:
React not displaying data after successful fetch
Objects are not valid as a React child. If you meant to render a collection of children, use an array instead
Currently, I'm trying to get my data to display from an API I developed. I'm used to the Angular approach which would call for a ngFor in the template for most data showcase situations.
I'm having trouble wrapping my mind around what I have to do here in order to display my data. The data is expected to be an array of objects which I would then parse to display.
I also receive the following error: Error: Objects are not valid as a React child (found: object with keys {data}). If you meant to render a collection of children, use an array instead.
I've searched high and low for a solution but sadly, nothing I've seen has worked for me. (All of the answers on SO are using the class-based version of React, of which I am not).
You can see my data output in the following screenshot:
I am also including my custom hook code and the component that is supposed to render the data:
CUSTOM DATA FETCH HOOK
interface Drone{
id: number;
name: string;
model: string;
price: number;
}
export function useGetData(urlpath:string) {
const [droneData, setData] = useState<any>()
async function handleDataFetch(path:string){
const result = await fetch(`https://drone-collections-api-jc.herokuapp.com${path}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'x-access-token': 'Bearer API-TOKEN'
}
})
const response = await result.json();
setData(response)
}
useEffect( () => {
handleDataFetch(urlpath)
})
return droneData
}
THE DRONE COMPONENT
import { useGetData } from '../../custom-hooks'
export const Drones = () => {
let data = useGetData('/drones')
console.log(data)
// const DisplayDrone = ( ) => {
// return (
// Array.prototype.map( data => {
// <div>{ data.name }</div>
// })
// )
// }
return (
<div>
<h1>Hello Drones</h1>
</div>
)
}
Also, for more context, the current code can be found at this repo: https://github.com/carter3689/testing-drone-frontend
Please, help me understand what I'm missing. Many Thanks!
There are several locations that needed to be fixed
In fetchData.tsx
export function useGetData(urlpath: string) {
const [droneData, setData] = useState<any>([]);
async function handleDataFetch(path: string) {
const result = await fetch(`https://jsonplaceholder.typicode.com/posts`, {
...
});
const response = await result.json();
setData(response);
}
useEffect(() => {
handleDataFetch(urlpath);
}, []);
Explanation:
you need a "blank" array for looping through. I guess that the error causes by the fact that at the start, before the data is fetched, there is nothing to loop through. It's same as doing undefined.map(), which is obviously fail.
You need a dependencies array for useEffect. Right now your code will do an infinite loop since everytime it get data, it update the state, thus re-run the useEffect and repeat. Add dependencies array limit when that useEffect will run
In Drones.tsx
return (
<div>
{data.map(item => <div>{item.name}</div>}
</div>
)
Not much to say here. I don't use Angular so I'm not sure why you use Array.prototype.map, but in React you can loop through your variable directly. I also have a CodeSandbox link for your project (I use public API)
I'm really stuck in trying to render some data being passed down as props. I'll include some code and definitions below, but if you feel that I need to include some further code snippets, please let me know (I'm really struggling to find what's causing the error, so I may have missed out the causal issue!).
I first take data from an API which is then used to populate a UserList component via useState (setUsers(data):
useEffect(() => {
async function getUserList() {
setLoading(true);
try {
const url =
"API URL";
const response = await fetch(url);
const data = await response.json();
setUsers(data);
} catch (error) {
throw new Error("User list unavailable");
}
setLoading(false);
}
getUserList();
}, []);
If a user is clicked in the UserList, this changes the selectedUser state of the parent Home component to be the specific user's unique_ID via:
onClick={() => setSelectedUser(unique_ID)}
If the selectedUser changes, the Home component also does a more updated data fetch from the API to get all information relevant to the specific user via their unique_ID:
useEffect(() => {
async function getSelectedUserData() {
try {
const url = `API URL/${selectedUser}`;
const response = await fetch(url);
const data = await response.json();
setSelectedUserData(data);
} catch (error) {
throw new Error("User data unavailable");
}
}
getSelectedUserData();
}, [selectedUser]);
The specific user data is then passed down as props to a child UserInformation component:
<UserInformation selectedUser={selectedUser} selectedUserData={selectedUserData} />
At this point, I can see all the data being passed around correctly in the browser React Developer Tools.
The UserInformation component then gets the data passed via props:
import React, { useEffect, useState } from "react";
function UserInformation({ selectedUser, selectedUserData }) {
const [currentUser, setCurrentUser] = useState({ selectedUserData });
useEffect(() => {
setCurrentUser({ selectedUserData });
}, [selectedUser, selectedUserData]);
return (
<div>
<p>{selectedUserData.User_Firstname}</p>
<p>{currentUser.User_Firstname}</p>
</div>
);
}
export default UserInformation;
And here is where I get stuck - I can't seem to render any of the data I pass down as props to the UserInformation component, even though I've tried a few different methods (hence the <p>{selectedUserData.User_Firstname}</p> and <p>{currentUser.User_Firstname}</p> to demonstrate).
I'd really appreciate any help you can give me with this - I must be making an error somewhere!
Thanks so much, and sorry for the super long post!
I managed to solve this (thanks to the help of Mohamed and Antonio above, as well as the reactiflux community).
import React from "react";
function UserInformation({ selectedUserData }) {
const currentUserRender = selectedUserData.map(
({ User_Firstname, User_Lastname }) => (
<div key={unique_ID}>
<p>{User_Firstname}</p>
</div>
)
);
return (
<div>
{selectedUserData ? currentUserRender : null}
</div>
);
}
export default UserInformation;
As selectedUserData was returning an array instead of an object, I needed to map the data rather than call it with an object method such as {selectedUserData.User_Firstname}.
const currentUserRender = selectedUserData.map(
({ User_Firstname, User_Lastname }) => (
<div key={unique_ID}>
<p>{User_Firstname}</p>
</div>
)
);
The above snippet maps the selected data properties found inside selectedUserData ({ User_Firstname, User_Lastname }), with the whole map being called in the return via {selectedUserData ? currentUserRender : null}.
Hopefully my explanation of the above solution is clear for anyone reading, and a big thanks again to Mohamed and Antonio (as well as a few others in the reactiflux Discord community) for helping!
You're trying to set the current user to an object with key "selectedUserData".
So if you want to access it you've to access it by this key name so change this line currentUser.User_Firstname to currentUser.selectedUserData.User_Firstname
i am looking to pass the json data that i received using the fetch API and use in the Useraccount component.
i have looked around and i can find a lot of material related to passing from child to parent and very few that mention from parent to child.
I have tried using this userinfo={credentailverify} and clearly it is not working for me, any suggestions please
Update3:
i have upload the small clip for the issue that i am facing for better understanding. i have tried to make the code very simple but still cant understand the reason why loginscreen is showing before showing the user account information.
youtube link showing issue
import Useraccount from "./Useraccount";
function Signin({ userinfo1, userinfo2 }) {
//userinfo1 is having customer account information
//userinfo2 is Boolean and showing if user is looged in or not if not then go to login page
return (
<div>
{userinfo2 ? (
<Useraccount userinfo={userinfo1} />
) : (
<SigninOptions />
)}
</div>
);
}
export default Signin;
Update2: i am also experience one strange thing as when i set setUserinfo and pass the new state into the child it does show the new state in child component there but here in main code if i try to console the userinfonew after its set it is showing me the initial state as empty array, is it some thing i am missing here!!
.then((data) => {
setUserinfo(data.data)
console.log(userinfonew)
}
-Note i can see if i run console.log(userinfonew) outside the Async function then it does show the updated status but not inside the async function, although i am updating the status inside the Async function. cant understand the reason behind it
Update1: initial problem is solved thanks and i have updated the code, now the only issue i am facing is the condition that i am using in the return statement is both getting executed i.e first for few seconds < SigninOptions /> component and then the correct one as per the logic < Useraccount userinfo={userinfonew} /> component not sure if there is a delay somewhere or code is runnig twice
function Signin() {
const [siginalready, setifsignedin] = useState(false);
const [userinfonew, setUserinfo] = useState([]);
useEffect(() => {
credentailverify();
}, []);
let url = "http://localhost:5000/api/verifyifloginalready";
let options = {
credentials: "include",
method: "POST",
};
let verifyifloginalready = new Request(url, options);
let credentailverify = async () => {
const x1 = await fetch(verifyifloginalready)
.then((res) => {
if (res.status == 400 || res.status == 401) {
return setifsignedin(false);
} else {
setifsignedin(true);
return res.json();
}
}).then((data)=>
{
// here the console is shoewing empty array
setUserinfo(data.data)
console.log(userinfonew)
})
.catch((err) => console.log("err"));
return x1;
};
return (
<div>
// here first <SigninOptions /> renders for a SECOND and then <Useraccount userinfo={userinfonew} />
{siginalready ? (
<Useraccount userinfo={userinfonew} />
) : (
<SigninOptions />
)}
</div>
);
}
export default Signin;
the below is the code at the user account,
import React, { useState, useEffect } from "react";
import "../App.css";
function Useraccount({ userinfo }) {
return <div>{ `The user email address is ${userinfo}`}</div>;
}
export default Useraccount;
and after the data is passed to the child component how can i use it, i have seen one place mentioned to use as this.props.userinfo but i am using React Hook so cant use this method to access.
Thanks in advance.
You need to access the props passed to child:
function Useraccount({ userinfo }) {
if (!userInfo) return <div />
return <div>{`The user email address is ${userinfo}`}</div>;
}
Also use a template string like I did above
My recommendation is to track the response of your response in the state, then pass that state value into the child component.
It can be helpful to think of an effect as happening in a different execution than your main code. Any data inside of there can only be communicated to your component through the functions that you pass in as the effect dependencies.