Why i can't access list of array in react - javascript

I fetch some date and get array and i can console.log and see all staff but when i try map or access of some elements from array i got error undefined...
and get this
Unhandled Rejection (TypeError): Cannot read property 'map' of undefined
const [key, setKey] = useState(false);
const [country, setCountry] = useState([]);
const [nextFive, setFive] = useState([]);
useEffect(() => {
getWeather();
}, [key]);
const getCity = async (e) => {
e.preventDefault();
const cityName = e.target.elements.cityName.value;
const api_call = await fetch(`http://dataservice.accuweather.com/locations/v1/cities/search?apikey=${process.env.REACT_APP_WEATHER_API_KEY}&q=${cityName}&details=true`);
const data = await api_call.json();
setKey(data[0].Key);
setCountry(data[0]);
return data[0].Key;
}
const getWeather = async (cityKey = key) => {
const proxy = `https://cors-anywhere.herokuapp.com/`;
const link = `${proxy}http://dataservice.accuweather.com/forecasts/v1/daily/5day/${cityKey}?apikey=${process.env.REACT_APP_WEATHER_API_KEY}&details=true&metric=true`;
const api_call = await fetch(link);
const data = await api_call.json();
setFive(data.DailyForecasts);
};
console.log(nextFive); if i tray console.log(nextFive[0].something) i will get error :)
console.log(country);
{
nextFive.map((item) => {
return <p>{item.Date}</p>
})
}
and get this Unhandled Rejection (TypeError): Cannot read property 'map' of undefined

This happends because you try to show first element of item in array that is not yet populated.
You could have extra hook called as example weatherLoading, once api call is succesfuly you would set status of weatherLoading to true and then simply do this:
weatherLoading && console.log(nextFive[0].something)

Related

Get firestore data as array

Im tryng to display firestore data but I just get one value. I have try forEach and map. Nothing is working. Heres my code:
React.useEffect(() => {
retrieveNetwork();
}, []);
const retrieveNetwork = async () => {
try {
const q = query(collection(db, "cities", uidx, "adress"));
const querySnapshot = await getDocs(q);
let result = [];
//querySnapshot.docs.map((doc) => setGas(doc.data().home));
querySnapshot.docs.map((doc) => {
result.push(doc.data().home);
setGas(result);
});
} catch (e) {
alert(e);
}
};```
The .map method returns an array (official docs here), so you could do something like this:
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
React.useEffect(() => {
retrieveNetwork();
}, []);
const retrieveNetwork = async () => {
try {
const q = query(collection(db, "cities", uidx, "adress"));
const querySnapshot = await getDocs(q);
// an array from the docs filled only with "home" data
const results = querySnapshot.docs.map((doc) => {
return doc.data().home;
});
// only update the state once per invokation
setGas(results)
} catch (e) {
alert(e);
}
};

react firestore snapshot not a function

Hi so I followed firebase docs to get data from my collection. But I get error that snapshot is undefined.
This is what I tried:
const [data, setData] = useState([]);
useEffect(() => {
const db = Firestore.firestore();
// let tempData = Controller.getData();
const projects = db.collection('projects');
const snapshot = projects.get();
let fireData = [];
snapshot.forEach((doc) => {
console.log('data:', doc.data());
fireData.push(doc.data());
});
setData(fireData);
});
I tried TypeError: snapshot.forEach is not a function
But then I het data() is undefined plz help
projects.get() returns a promise that resolves with a QuerySnapshot object. It doesn't directly return the snapshot. You have to wait for the promise to resolve in order to get the data, as illustrated in the documentation:
projects.get().then(snapshot => {
let fireData = [];
snapshot.forEach((doc) => {
console.log('data:', doc.data());
fireData.push(doc.data());
});
setData(fireData);
});

How to fetch data from multiple urls at once?

I have a function that fetches from a url in React
const DataContextProvider = (props) => {
const [isLoading, setLoading] = useState(false);
const [cocktails, setCocktails] = useState([]);
useEffect(() => {
const fetchCocktailList = async () => {
const baseUrl = 'https://www.thecocktaildb.com/api/json/v1/1/';
setLoading(true);
try {
const res = await fetch(`${baseUrl}search.php?s=margarita`);
const data = await res.json();
console.log(data);
setCocktails(data.drinks);
setLoading(false);
} catch (err) {
console.log('Error fetching data');
setLoading(false);
}
};
fetchCocktailList();
}, []);
How I'm mapping data so far.
const DrinkList = () => {
const { cocktails } = useContext(DataContext);
return (
<div className='drink-list-wrapper'>
{cocktails.length > 0 &&
cocktails.map((drink) => {
return <DrinkItem drink={drink} key={drink.idDrink} />;
})}
</div>
);
};
However I want to fetch from this url also ${baseUrl}search.php?s=martini
I would like a good clean way to do this and set my state to both of the returned data.
First base the data fetch function on a parameter:
const fetchCocktail = async (name) => {
const baseUrl = 'https://www.thecocktaildb.com/api/json/v1/1/';
try {
const res = await fetch(`${baseUrl}search.php?s=` + name);
const data = await res.json();
return data.drinks;
} catch (err) {
console.log('Error fetching data');
}
}
Then use Promise.all to await all results:
setLoading(true);
var promises = [
fetchCocktail(`margarita`),
fetchCocktail(`martini`)
];
var results = await Promise.all(promises);
setLoading(false);
DrinkList(results);
Where results will be an array with the responses that you can use on the DrinkList function.
Here's a method which will let you specify the cocktail names as dependencies to the useEffect so you can store them in your state and fetch new drink lists if you want new recipes. If not, it'll just be a static state variable.
I've also added another state variable errorMessage which you use to pass an error message in the case of failure.
Also, you should include the appropriate dependencies in your useEffect hook. The setState functions returned by calls to useState are stable and won't trigger a re-run of the effect, and the cocktailNames variable won't trigger a re-run unless you update it with new things to fetch.
const DataContextProvider = (props) => {
const [isLoading, setLoading] = useState(false);
const [cocktails, setCocktails] = useState([]);
const [errorMessage, setErrorMessage] = useState(''); // holds an error message in case the network request dosn't succeed
const [cocktailNames, setCocktailNames] = useState(['margarita', 'martini']); // the search queries for the `s` parameter at your API endpoint
useEffect(() => {
const fetchCocktailLists = async (...cocktailNames) => {
const fetchCocktailList = async (cocktailName) => {
const baseUrl = 'https://www.thecocktaildb.com/api/json/v1/1/search.php';
const url = new URL(baseUrl);
const params = new URLSearchParams({s: cocktailName});
url.search = params.toString(); // -> '?s=cocktailName'
const res = await fetch(url.href); // -> 'https://www.thecocktaildb.com/api/json/v1/1/search.php?s=cocktailName'
const data = await res.json();
const {drinks: drinkList} = data; // destructured form of: const drinkList = data.drinks;
return drinkList;
};
setLoading(true);
try {
const promises = [];
for (const cocktailName of cocktailNames) {
promises.push(fetchCocktailList(cocktailName));
}
const drinkLists = await Promise.all(promises); // -> [[drink1, drink2], [drink3, drink4]]
const allDrinks = drinkLists.flat(1); // -> [drink1, drink2, drink3, drink4]
setCocktails(allDrinks);
}
catch (err) {
setErrorMessage(err.message /* or whatever custom message you want */);
}
setLoading(false);
};
fetchCocktailList(...cocktailNames);
}, [cocktailNames, setCocktails, setErrorMessage, setLoading]);
};
var promises = [
fetchCocktail(api1),
fetchCocktail(api2)
];
var results = await Promise.allSettled(promises);

use date from fetch object to create link for fetch another object in react

I write some code and works but i think maybe can be done on some better way. What i want from code? I create link and fetch object from that object i want use some value and pass that value in another link after that fetch new object. My code working but i want see if is possible new solution.
const [key, setKey] = useState("");
const [data, setData] = useState([]);
useEffect(() => {
getKey();
getWeather();
},[key]);
//this function get key from object and that key i will use in another link
const getKey = () => {
navigator.geolocation.getCurrentPosition(
(position) => {
const long = JSON.stringify(position.coords.longitude);
const lat = JSON.stringify(position.coords.latitude);
const proxy = `https://cors-anywhere.herokuapp.com/`;
const link = `${proxy}http://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=rhlYEhvAu0nhFNMFybOIhffbmjFX0AZN&q=${lat}%2C${long}&details=true`;
(async function fetchData(){
const getValue = await fetch (link);
const key = await getValue.json();
setKey(key.Key);
})();
}
);
};
const getWeather = async () => {
const proxy = `https://cors-anywhere.herokuapp.com/`;
const link = `${proxy}http://dataservice.accuweather.com/forecasts/v1/daily/5day/${key}?apikey=rhlYEhvAu0nhFNMFybOIhffbmjFX0AZN&details=true&metric=true`;
const data = await fetch (link);
const getData = await data.json();
setData(getData);
};
You can make this work by just making a few slight changes to your code. Make the useEffect and async function, return the key from getKey to a variable and await the variable assignment and pass to getWeather. Something like this:
const [key, setKey] = useState("");
const [data, setData] = useState([]);
useEffect(async() => { // <---- Converted to async
const apiKey = getKey(); // <---- Assigned to variable
getWeather(await apiKey); // <--- Using apiKey in function rather than just state
},[key]);
const getKey = () => {
navigator.geolocation.getCurrentPosition(
(position) => {
const long = JSON.stringify(position.coords.longitude);
const lat = JSON.stringify(position.coords.latitude);
const proxy = `https://cors-anywhere.herokuapp.com/`;
const link = `${proxy}http://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=rhlYEhvAu0nhFNMFybOIhffbmjFX0AZN&q=${lat}%2C${long}&details=true`;
(async function fetchData(){
const getValue = await fetch (link);
const key = await getValue.json();
setKey(key.Key);
return key.Key //<------ returned key for useEffect
})();
}
);
};
const getWeather = async (apiKey = key) => { // <----If no value passed to function, will use state value
const proxy = `https://cors-anywhere.herokuapp.com/`;
const link = `${proxy}http://dataservice.accuweather.com/forecasts/v1/daily/5day/${apiKey}?apikey=rhlYEhvAu0nhFNMFybOIhffbmjFX0AZN&details=true&metric=true`;
const data = await fetch (link);
const getData = await data.json();
setData(getData);
};
The reason I returned the value rather than using state is because setting state is asynchronous and there is currently no callback function for the useState setting function like there was for setState.

Firebase: snapshot.val is not a function or its return value is not iterable

So I am creating a small application using Express, Firebase and ReactJS.
I stubmled upon issue with Firebase snapshot.val
This part of code on express is serving my trips by ID and to get all. For all trips is working fine but when I try to go to the single trip it returns me this error
if I destructure it as an array:
UnhandledPromiseRejectionWarning: TypeError: snapshot.val is not a function or its return value is not
iterable
If I destructure it as a object:
TypeError: Cannot destructure property trip of 'undefined' or
'null'.
and I am totally baffled and can't find the solution.
Here is express part of the code:
const firebase = require('firebase');
const reference = () => firebase.database().ref('/trips')
exports.getSingle = async (id) => {
//const snapshot = await reference()
// .orderByChild('id')
// .equalTo(id)
// .limitToFirst(1)
// .once('value');
// const [trip] = snapshot.val();
// return trip
const snapshot = await reference()
.orderByChild('id')
.equalTo(parseInt(id, 10))
.limitToFirst(1)
.once('value');
let trip = [];
snapshot.forEach(function (tripSnapshot) {
tripStorage = tripSnapshot.val();
console.log(tripStorage);
trip.push(tripStorage);
});
console.log(trip);
return trip;
};
exports.getAll = async () => {
const snapshot = await reference().once('value');
return snapshot.val();
};
I am fetching trips in the frontend with:
componentDidMount() {
fetch("/api/trips/get/" + this.state.id)
.then(res => res.json())
.then(trip =>
this.setState({
trip
}, () => console.log("Trips fetched...", trip))
);
}
Controller for trips:
const express = require('express');
const route = express.Router();
const tripUtil = require('../utils/tripUtil');
route.get('/getAll', async (req, res) => {
const trips = await tripUtil.getAll();
res.json(trips);
});
route.get('/get/:id', async (req, res) => {
const { id } = req.params;
const trip = await tripUtil.getSingle(id);
if (trip) {
res.json(trip);
} else {
res.status = 400;
res.json({
error: 'trip not found'
});
}
});
module.exports = route;
And maping properties from object like this:
{this.state.trip.itinerary.days.map((day, i) => {
return (
<div key={i} className="trip-single-item__day">
<div className="trip-single-item__day-header">
<div className="trip-single-item__day-title">
{day.title}
</div>
<div className="trip-single-item__day-subtitle">
{day.subtitle}
</div>
<div className="trip-single-item__day-date">
{day.date}
</div>
</div>
<div className="trip-single-item__day-body">
<div className="trip-single-item__day-descritpion">
{day.description}
</div>
</div>
</div>
);
})}
Here is image of single trip Single trip in firebase
Any suggestions or directions? Thanks
Firebase generally recommends against storing data as arrays in the Realtime Database (though some exceptions exist): https://firebase.googleblog.com/2014/04/best-practices-arrays-in-firebase.html
What I'm guessing is going on is that for some reason, your snapshot value is not being rendered as an array, but an object with numeric keys. One reason for this might be if the keys in Firebase are not strictly sequential, as Firebase will only render your data as an array if keys of the data start with 0 and there are no gaps.
Can you try this code to see if it works?
exports.getSingle = async (id) => {
const snapshot = await reference()
.orderByChild('id')
.equalTo(id)
.limitToFirst(1)
.once('value');
const queryResult = snapshot.val()
console.log(queryResult) // Make note of the object shape here. Make sure there are actually results.
const trip = Object.values(queryResult)[0]
return trip;
};
Might be an UnhandledPromiseRejectionWarning.
Handle your snapshot with then/catch
exports.getSingle = async (id) => {
await reference()
.orderByChild('id')
.equalTo(id)
.limitToFirst(1)
.once('value').then(snapshot => {
const [trip] = snapshot.val();
return trip
// console.log(post.id) if required
})
};

Categories