a little help I'm new in React
Why the useEffectworks fine, but the
data.name
render fines, but the e.g
data.main.lat
he dont have acess to, even with map.
The data in the child he can't get it
I hope this question make sense 🙂
import React, { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
import axios from "axios";
const API_KEY = ''
function Home() {
const [data, setData] = useState([]);
const location = useLocation();
let val = location.pathname.slice(1)
useEffect(() => {
axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${val}&appid=${API_KEY}`)
.then((response) => {
// console.log(response);
setData(response.data)
}).catch((error) => {
// console.error("Error fetching data: ", error);
// setError(error);
});
}, []);
return (
<div>
<h2>Home</h2>
<p>{data.name}</p>
{
data.main.map((i) => {
<h1>{i.lat}</h1>
})
}
</div>
);
};
response
{
base: "stations"
clouds:
all: 0
[[Prototype]]: Object
cod: 200
coord: {lon: 2.3488, lat: 48.8534}
dt: 1646247623
id: 2988507
main:
feels_like: 281.92
humidity: 65
pressure: 1019
temp: 282.42
temp_max: 283.03
temp_min: 281.41
[[Prototype]]: Object
name: "Paris"
sys:
country: "FR"
id: 6550
sunrise: 1646202672
sunset: 1646242483
type: 1
[[Prototype]]: Object
timezone: 3600
visibility: 10000
weather: Array(1)
0: {id: 800, main: 'Clear', description: 'clear sky', icon: '01n'}
length: 1
[[Prototype]]: Array(0)
wind:
deg: 100
speed: 1.54
How i have access to <p> {data.coord.lat}</p> in the return. this shows error Cannot read properties of undefined (reading 'lat')
When your component first renders, data is an empty array. Since arrays don't have a main property, you'll get an error because you're trying to use a map function of undefined.
What you can do is
{
data?.main?.map((i) => (
<h1>{i.lat}</h1>
))
}
Which will only call the map function if data.main exists. Also, make sure that data.main in your response is an array as well, otherwise map won't work
You can try to check data.main.length
{
data.main && data.main.map((i) => {
<h1>{i.lat}</h1>
})
}
You're not returning <h1>{i.lat}</h1> in the callback to data.main.map() method.
Either add a return statement:
data.main.map((i) => {
return <h1>{i.lat}</h1>
})
Alternatively remove the curly brackets to implicitly return:
data.main.map((i) => <h1>{i.lat}</h1>)
See Array.map on MDN
According to open weather API doc main you're trying to map is an object, not an array.
Instead of map simply access it as an object : <p>{data.coord.lat}</p>
lat element exist in coord not main object.
3.API call take some time to provide data and you're mapping state which is still empty when there's no response from API maybe you can add loading state when you're waiting response.
Live Demo: https://codesandbox.io/s/awesome-engelbart-gxzqcd?file=/src/App.js
Related
I'm trying to render a page with some details I get from a api call.
useEffect(() =>{
getCards();
}, [])
const [userCards, setCards] = useState([])
const getCards = async (event) => {
let token = localStorage.getItem("user");
await api
.get("/fetch-card-balance",
{headers:{"token":`${token}`}})
.then((response) => {
console.log(response);
if (response.data.success === false) {
toast.error(response.data.message);
setCards(false);
} else if (response.data.success === true) {
console.log(response.data.payload)
setCards(response.data.payload)
}
})
.catch((err) => {
toast.error(err.response.data.message);
});
};
console.log(userCards)
Here userCards is logged as
[
{
balance: 0.00,
cifNumber: "0001111222",
createdAt: "2021-08-03T12:19:51.000Z",
first6: "123456",
id: 1234,
last4: "7890"
},
{
balance: 20.00,
cifNumber: "0002222333",
createdAt: "2021-07-03T12:19:51.000Z",
first6: "234567",
id: 2345,
last4: "8901"
}
]
Then I try to use forEach to filter the properties I need
const cardDetails = []
userCards.forEach(option => cardDetails.push(
{
cardNumber: `${option.first6}******${option.last4}`,
balance: `${option.balance}`
}
))
But when I run
console.log(cardDetails[0].balance)
I get "Uncaught TypeError: Cannot read property 'balance' of undefined". I've gone over it several times and the only conclusion I have is that I'm missing something that may not be so obvious. Could someone help point out what it is.
Using cardDetails[0].balance will only work when there is at least one element in cardDetails. Otherwise getting the first element in the array yields undefined, causing your error message. Since you only fill the array after the API request returns, at least your first render will be done with an empty array.
An easy way to handle this would be checking for if (cardDetails.length > 0) first.
Try this out
const cardDetails = userCards.map(function(option) { return {cardNumber: ${option.first6}******${option.last4}, balance: ${option.balance}}});
Background: I am using reactjs and my goal is to get data stored in firestore
i have an empty array which i am adding to, after which the array is returned
import firebase from "firebase/app"
import "firebase/storage"
import "firebase/firestore"
class firebaseclass {
constructor() {
if (firebase.apps.length === 0) {
firebase.initializeApp(firebaseConfig);
}
}
getfirestore() {
const db = firebase.firestore();
var storearray = []
db.collection("data").get().then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
storearray.push(doc.data())
});
});
return storearray
}
}
export default new firebaseclass
In my homepage, i am calling the function as such
function App() {
const [names, setnames] = useState({
})
var datadata = firebaseclass.getfirestore();
console.log(datadata, "datadatadata")
return (
<div className="App">
</div>
);
}
export default App;
this is the console log result
[]
0: {hasstart: false, lat: ******, long: *******, name: "***", postalcode: ******, …}
1: {hasstart: false, lat: ******, long: ******, name: "chias crib", postalcode: ******, …}
2: {hasstart: false, lat: "test3", long: "test3", postalcode: 3, powerlevel: 3, …}
length: 3
__proto__: `Array(0)
"data data data"`
as you can see, i am receiving some form of result
when i
console.log(typeof(datadata), "data data data");
i receive
object data data data
which is odd, because i returned an array perviously
finally, when i try
console.log(datadata.length, "data data data");
i receive
0 "data data data"
i have also tried
var size = Object.keys(datadata).length;
console.log(size, "datadatadata")
but i have received
0 "datadatadata"
i have also tried changing the empty array to an object, and returning an object instead of an array, but the above methods still do not work. What am i doing wrong here?
Your problem is that you return an empty array each time because the part where you're adding into array is done asynchronously.
getfirestore() {
const db = firebase.firestore();
var storearray = []
// THIS PART RUNS ASYNC.
db.collection("data").get().then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
storearray.push(doc.data()). //. << ------ THIS IS CALLED SECOND
});
});
return storearray // <<------- THIS IS CALLED FIRST
}
You will have to return a callback, a promise or use async/await
Update for async/await approach should be something like this:
async getfirestore() {
const db = firebase.firestore();
const storearray = [];
const querySnapshot = await db.collection("data").get();
querySnapshot.forEach(doc => { storearray.push(doc.data()) });
return storearray;
}
Then in your App:
async function App() {
const [names, setnames] = useState({
})
var datadata = await firebaseclass.getfirestore();
console.log(datadata, "datadatadata")
...
First of Arrays are objects in JS.
Next if you notice the first line in your console, is an empty array.
[]
0: {hasstart: false, lat: ******, long: *******, name: "***", postalcode: ******, …}
1: {hasstart: false, lat: ******, long: ******, name: "chias crib", postalcode: ******, …}
2: {hasstart: false, lat: "test3", long: "test3", postalcode: 3, powerlevel: 3, …}
length: 3
__proto__: `Array(0)
"data data data"`
That's why you receive count 0, since the expected output comes last. Basically JS wont wait for the server, unless it was set to async. Please have a look at this it might give you more idea what I am talking about.
So I'm new to all react and redux thing and after few tuts and blogs, I'm trying to create a react-redux app. I'm hitting an API to fetch some data.
reducer looks like this:
const initialState ={
rest: null,
rests: null,
loading: false
}
export default function(state = initialState, action){
switch (action.type){
case REST_LOADING:
return{
...state,
loading: true
}
case GET_REST:
return {
...state,
rest: action.payload,
loading: false
}
Action:
export const getCurrentRest = name =>
dispatch(setRestLoading());
axios.get(`/api/restaurant/rest/${name}`)
.then(res =>
dispatch({
type: GET_REST,
payload: res.data
})
)
.catch(err =>
dispatch({
type: GET_REST,
payload: {}
})
);
}
Now I'm calling this action on a page something like this:
class CafeMenu extends Component {
name = this.props.match.params.id;
constructor(props) {
super(props);
}
componentDidMount() {
this.props.getCurrentRest(this.name);
}
in the render part, I did some destructuring.
initially:
const {rest, loading} = this.props.rest;
later i changed it to
const {rest} = this.porps.rest;
now I can see the data in the console logs and state changes in redux devtools extension but when I try to access it through rest.name or this.rest.rest.name without destructuring it throws typeError, say cannot read property 'rest' of undefined. I tried everything but couldn't figure out what I did wrong and how to resolve this further and stuck at this.
initially i also did something like :
if(rest === undefined || null){
<h1> loading</h1>
}
else{
reder...
and the console.log of this.props.rest is
{rest: Array(1), rests:null, loading: false}
loading: false
rest: Array(1)
0:
email: "something#abc.com"
loc: {long: "23.34", lat: "43"}
loc_name: "abc"
menu: []
name: "xyz"
...
Looks like you're not properly destructing your props.
Also there's a typo here porps instead of props.
const { rest } = this.porps.rest;
This can be the reason it throws typeError.
Now for destructing part. Assuming this is your props structure
{rest: Array(1), rests:null, loading: false}
loading: false
rest: Array(1)
0:
email: "something#abc.com"
loc: {long: "23.34", lat: "43"}
loc_name: "abc"
menu: []
name: "xyz"
rests: null
Here's to access via destructing:
const { loading, rest, rests } = this.props
The values will be
loading = false
rest = Array(1)
0:
email: "something#abc.com"
loc: {long: "23.34", lat: "43"}
loc_name: "abc"
menu: []
name: "xyz"
rests = null
Now since rest is an array, to access the first restaurant name the rest[0].name should give you "xyz".
Let me know if this helps.
I passed Array but got Observer here's my code:
In Component1
data() {
return {
myWords: [],
}
}
//...
await axios.post(this.serverUrl + router, {
voca: text,
category: this.buttonGroup.category.text
})
.then(res => {
this.myWords.push({
voca: this.voca,
vocaHeader: this.vocaHeader,
category: res.data.savedVoca.category,
date: res.data.savedVoca.date,
id: res.data.savedVoca._id
})
this.myWords.push({voca:"test"})
})
.catch(err => {
console.log(err)
})
In Component2
props: {
myWordsProp: {
type: Array,
default: () => ([])
},
},
mounted() {
console.log(this.myWordsProp)
console.log(this.myWordsProp[0]) //returns undefined
},
And I expected an Array but I get Observer so I can't get values from this.myWordsProp[0] why?
//this.myWordsProp
[__ob__: Observer]
0: {
category: "ETC"
date: "2018-11-21T15:31:28.648Z"
id: "5bf57a503edf4e0016800cde"
voca: Array(1)
vocaHeader: Array(1)
...
}
1: {__ob__: Observer}
length: 2
__ob__: Observer {value: Array(2), dep: Dep, vmCount: 0}
__proto__: Array
//this.myWordsProp[0]
undefined
I found a clue that when I test it outside of axios it worked as I expected.
Vue wraps data and props into reactive objects. Use vue-devtools plugin in your browser as an alternative to viewing the ugly observer in the console.
In your code, the object behaves correctly. It’s only in the console that it ‘looks’ different.
Anyway, you can also click on the ... to expand the node and get the value from the console.
https://github.com/vuejs/vue-devtools
I found a solution It's because of sending props before get data from server.
This is my whole of postVocas function It returns promise
postVocas: function (voca) {
if (!voca || voca.length < 1) return
let router = "/api/voca"
let text = ""
text += `${this.vocaHeader[0].english}, ${this.vocaHeader[0].korean}\n`
voca.forEach((x, index) => {
text += `${voca[index].english}, ${voca[index].korean}\n`
})
return axios.post(this.serverUrl + router, {
voca: text,
category: this.buttonGroup.category.text
}).then(res => {
this.myWords.push({
voca: this.voca,
vocaHeader: this.vocaHeader,
category: res.data.savedVoca.category,
date: res.data.savedVoca.date,
id: res.data.savedVoca._id
})
}).catch(err => {
console.log(err)
})
},
And await till get data from server.
This one is function where execute My postVocas function.
sendVocaToTable: async function () {
let reformedText = this.reformText(this.text)
this.voca = this.formatTextToVoca(reformedText)
await this.postVocas(this.voca)
this.$router.push({
name: 'Table',
params: {
vocaProp: this.voca,
tableHeaderProp: this.vocaHeader
}
})
},
I'm trying to map over an API response but it gives me "TypeError: response.map is not a function".
I think it might be due to the map method getting a string instead of an array. but when I console.log it gives me an array so I can't really see where the error comes from.
Or maybe I'm accessing the API response array in a wrong way.
I've read tons of documentation and threads but still can't get what am I doing wrong.
Thanks in advance
{status: {…}, outputs: Array(1), rawData: {…}}
outputs: Array(1)
0:
created_at:"2018-08-24T19:58:44.351091715Z"
data:
concepts:Array(20)
0:{id: "ai_69gDDQgl", name: "hamburger", value: 0.9955255, app_id: "main"}
1:{id: "ai_QLn2rxmZ", name: "lettuce", value: 0.9920815, app_id: "main"}
const IngredientsList = ({ response }) => {
const items = response.map((item) =>
<ul>{item}</ul>)
return (
<div>
<p>{items}</p>
</div>
)
}
This would render the concepts array with name and value in a list?
const IngredientsList = ({ response }) => {
if (!response || !response.outputs) {
return null;
}
const items = response.outputs[0].data.concepts.map((item) => <li>name: {item.name}, value: {item.value}</li>);
return (
<div>
<ul>{items}</ul>
</div>
)
}