I am using axios to fetch and retrieve data from an api. I then set the api data to some state. When I save the changes, it shows the name from index [0] of the array, as I want. However when I refresh the page, it throws an error "Cannot read properties of undefined (reading 'name')". It seems like I am losing the api data when I refresh, what am I doing wrong here? The api endpoint is madeup, as I don't want to share the real endpoint here.
const [apiData, setApiData] = useState([]);
useEffect(() => {
axios
.get("https://some-api")
.then((res) => {
setApiData(res.data);
})
.catch((error) => {
alert(error.message);
});
}, []);
return <h1>{apiData[0].name}</h1>
try it please
return <h1>{ aapiData.length && apiData[0]?.name}</h1>
It has nothing to do with refreshing the page. This will always happen on the first render for this component.
The initial state value is an empty array:
const [apiData, setApiData] = useState([]);
And you're trying to access the first element of that array:
return <h1>{apiData[0].name}</h1>
An empty array has no first element, so it throws an error.
You can use optional chaining:
return <h1>{apiData[0]?.name}</h1>
Or perhaps check if it's there before using it:
return <h1>{apiData[0] && apiData[0].name}</h1>
Or not render anything at all if the array is empty:
return apiData.length > 0 ? <h1>{apiData[0].name}</h1> : null;
Related
The following is my function in which I am trying to filter notes from my Firestore collection by user.uid:
const fetchPost = async () => {
const queryConstraints = []
if (user.uid != null) queryConstraints.push(where('userID', '==', user.uid))
if (user.email != null) queryConstraints.push(where('userEmail', '==',
user.email))
const q = query(collection(db, 'notes'), ...queryConstraints)
await getDocs(collection(q))
.then((querySnapshot)=>{
const newData = querySnapshot.docs
.map((doc) => ({...doc.data(), id:doc.id }));
setNotes(newData);
console.log(notes, newData);
})
}
Yet, when I try and run this code in the browser, regardless if it is Chrome, Safari, Firefox, I am getting the following error:
Uncaught (in promise) TypeError: Cannot read properties of null
(reading 'uid')
at fetchPost (NoteList.js:66:1)
I am not sure how to fix this error, or really the right question to ask over how ask over why this this is happening.
Previously, I was using the following function:
const fetchPost = async () => {
await getDocs(collection(db, "notes"))
.where('userEmail', '==', user.email)
.then((querySnapshot)=>{
const newData = querySnapshot.docs
.map((doc) => ({...doc.data(), id:doc.id }));
setNotes(newData);
console.log(notes, newData);
})
}
I was getting the same error previously stated, but at least with this function, if I made any change to user.uid, React's hot reload would refresh the page, and it would bring in the queried data in the console, yet when I F5 the page, the data was not showing with the error message listed above.
Also, when I use the query builder in Firestore with the same parameters, I am able to pull the data I want to display, it is just not pulling/displaying as wanted in my react app.
Any help would be appreciated.
You are getting this error because your user is null.
Try to print value of user before accessing id. 👈 You'll know the value of user
Try to wrap your user.id with if(user) user.id. 👈 This should remove your erros
I have a very simple react code, which I use to track containers location on a territory. After a new container get's into the territory I have props.operationsList changed. So I send get response to server API when props.operationsList changes
useEffect(() => {
async function fetchContainerLocation() {
const response = await CoordinatesService.getContainersPosition()
console.log('response = ', response.data.features)
setContainersList(response.data.features)
console.log('containersList = ', containersList)
}
fetchContainerLocation()
}, [props.operationsList])
I need to update containersList const, that I use to rerender a map API where I should locate the containers. I define it like that:
const [containersList, setContainersList] = useState([])
I need to set containersList in accordance with that response fron server (response.data.features) to make my map rerender. What's strange,
console.log('response = ', response.data.features)
shows accurate and correct data from server, but the next
console.log('containersList = ', containersList)
is not equal with this response
Instad of getting the map rendered with the right data from server response, I have wrong data. So, I do now understand why such an straightforward approch do not work and how to fix it
State updates in React are asynchronous; when an update is requested, there is no guarantee that the updates will be made immediately.
So, try to log your containersList outside useEffect and compare both logs. both should be same.
TIP: While using map method with your containerList use it like containerList?.map() so that page does not turn out to be blank.
const fetchContainerLocation = async () => {
const response = await CoordinatesService.getContainersPosition();
console.log("response = ", response.data.features);
setContainersList(response.data.features);
};
useEffect(() => {
fetchContainerLocation();
}, [props.operationsList]);
console.log(containerList);
return (
<>
{containerList?.map((container) => (
<p>something you want to render</p>
))}
</>
);
No idea why, but it worked when I changed response.data.features
to [...response.data.features]
Working code
useEffect(() => {
async function fetchContainerLocation() {
setContainersList([])
const response = await CoordinatesService.getContainersPosition()
setContainersList([...response.data.features])
}
fetchContainerLocation()
}, [props.operationsList])
If anybody could explain why, it would be useful
I trying to get the url of an image from firebase firestore, the upload goes successfully, but when I try to get the image and display it, it gives me this error:
TypeError: undefined is not an object (evaluating 'doc.push')
This is the method how I get datas:
useEffect(() => {
const result = db.collection("homeBackground").doc("bg_img").onSnapshot(snap => {
const doc = snap.doc;
setImages(doc.push(doc => doc.data()))
});
return result;
}, []);
This is how I display the image:
{
images.push((image) => {
return (
<img src={image.imageUrl} alt=""/>
)
})
}
onSnapshot() is a kind of asynchronous Observable method. You need to check are data exist then assign to any thing you want.
.onSnapshot(snap => {
if(snap.exists()) { // Some methods returns object with .exist value/function.
setImages(snap.data())
}
});
onSnapshot(snapshoot: QuerySnapshot<DocumentData> | DocumentSnapshot<DocumentData> => ...) This is what you get depending on on what you wariong on you can get generic object of querysnapshot or DocumentSnapshot.
This method will trigger when data will change in database. It returns unsubscription which you can invoke and stop observing database changes.
const unsubscription = db.collection("homeBackground").doc("bg_img").onSnapshot(snap => ...);
// later in time
unsubscription() // this will stop invoke method above.
I am new to react and I try to get data from the backend and view that data in the frontend. To do that I tried this code:
function VProfile() {
const buyerId=(localStorage.getItem("userId"));
console.log(buyerId);
const [buyerDetails, setBuyerDetails] = useState({});
useEffect(() => {
axios
.get(`/getBuyerDetails`)
.then((response) => setBuyerDetails(response.data.existingBuyers))
.catch((err) => console.error(err));
}, []);
console.log(buyerDetails);
const oneBuyer = buyerDetails?.find(oneBuyer => oneBuyer.buyerId === buyerId);
console.log(oneBuyer);
}
When I call the API I get a length 3 array of objects. This is an image of the data.
Then I try to find the data of a specific buyer using the find function. To do that I use this const oneBuyer = buyerDetails?.find(oneBuyer => oneBuyer.buyerId === buyerId) code. But then I got an error that says TypeError: buyerDetails.find is not a function. How do I silve this problem?
Thank You!
You initialize the state with an object ({}):
const [buyerDetails, setBuyerDetails] = useState({});
Objects don't have .find, only arrays do.
You might want to initialize it to an empty array ([]), or undefined to more clearly signal the case that the data is not yet loaded.
You call setBuyerDetails asynchronously, so at the moment when you log them, the data hasn't arrived, due to console.log you are being "deceived" to believe the data is there. From docs:
Please be warned that if you log objects in the latest versions of
Chrome and Firefox what you get logged on the console is a reference
to the object, which is not necessarily the 'value' of the object at
the moment in time you call console.log(), but it is the value of the
object at the moment you open the console.
To avoid that error you can initialize that variable with empty array initially:
const [buyerDetails, setBuyerDetails] = useState([]);
Do it something like that and initialize with an array and you are using async operations so please add the loading flag
function VProfile() {
const buyerId=(localStorage.getItem("userId"));
console.log(buyerId);
const [buyerDetails, setBuyerDetails] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
axios
.get(`/getBuyerDetails`)
.then((response) => {
setBuyerDetails(response.data.existingBuyers));
setLoading(false);
})
.catch((err) => console.error(err));
}, []);
console.log(buyerDetails);
if (loading) return <p>loading....</p>
const oneBuyer = buyerDetails?.find(oneBuyer => oneBuyer.buyerId === buyerId);
console.log(oneBuyer);
}
Ultimate goal is to store the JSON data. That way, if the same github user is sent to the GitHubUser component, instead of making a fresh call to the API, it should load the details from the local storage, preventing a network call.
Key Points about the problem.
do a simple fetch from github public api (no issues, working fine)
store the data to local storage with the github username as key (not working)
retrieve the data from local storage by providing a github username as key (not working)
display json data after render is complete using useEffect (working fine)
I get no errors of any kind with localStorage but nothing gets saved. I have tried this on both Firefox and Edge. The network call happens on every change of login, for the same user, which it should not.
Further, this code is from a textbook I am following, and this is a exact copy from the page that discusses fetch and useEffect. The author goes on to explain that it should work and so far the book has been correct with no errors.
I have put the code in a sandbox here - https://codesandbox.io/s/bold-http-8f2cs
Also, the specific code below.
import React, { useState, useEffect } from "react";
const loadJSON = key =>
key && JSON.parse(localStorage.getItem(key));
const saveJSON = (key, data) =>
localStorage.setItem(key, JSON.stringify(data));
function GitHubUser({ login }) {
const [data, setData] = useState(
loadJSON(`user:${login}`)
);
useEffect(() => {
if (!data) return;
if (data.login === login) return;
const { name, avatar_url, location } = data;
saveJSON(`user:${login}`, {
name,
login,
avatar_url,
location
});
}, [data]);
useEffect(() => {
if (!login) return;
if (data && data.login === login) return;
fetch(`https://api.github.com/users/${login}`)
.then(response => response.json())
.then(setData)
.catch(console.error);
}, [login]);
if (data)
return <pre>{JSON.stringify(data, null, 2)}</pre>;
return null;
}
//Jay-study-nildana
//MoonHighway
export default function App() {
return <GitHubUser login="Jay-study-nildana" />;
}
Note : I get a couple of warnings related to useEffect but I have already isolated that they are not the issue but I dont think they are the problem. it simple tells me not to use a dependency array since there is only one element for both useEffects. I am using the array on purpose.
Update 1
One thing I noticed is, in developer tools, nothing is getting stored in Local Storage after a successfull call to the API. So, right now, I am thinking, saving is not working. Unless I get that working and see the stored data in developer tools, I wont know if load is working or not.
First, if the initial state is the result of some computation, you may provide a function instead, which will be executed only on the initial render:
// instead of this
const [data, setData] = useState(
loadJSON(`user:${login}`)
);
// you better have this
const [data, setData] = useState(() => {
return loadJSON(`user:${login}`);
});
Second, you can achieve what you need with this single useEffect:
const [data, setData] = useState(() => { return loadJSON(`user:${login}`); });
useEffect(() => {
if (!data) {
fetch(`https://api.github.com/users/${login}`)
.then((response) => response.json())
.then((val) => {
saveJSON(`user:${login}`, val); // put data into localStorage
setData(val); // update React's component state
})
.catch(console.error);
}
});
if (data) return <pre>{JSON.stringify(data, null, 2)}</pre>;
return <div>no data</div>;
You will get your data in localStorage. Don't forget that you need to use key user:${login} if you need to get it from there.