Unable to get object/array size - javascript

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.

Related

React functional: Cannot read properties of undefined, API. Acess to child

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

Filter out the fields in API response in React using fetch API

I have API response coming like below, I need to filter it out so that I should only get few fields out of this. e.g. I just need description and placeLocation.
results: [{placeId: "BHLLC", placeLocation: "BUFR", locationType: "BUFR",…},…]
0: {placeId: "BHLLC", placeLocation: "BUFR", locationType: "BUFR",…}
binControl: "Y"
description: "BUFR - Good Stock"
locationType: "BUFR"
ours: "Y"
placeId: "BHLLC"
placeLocation: "BUFR"
transferIsUse: "Y"
usable: "F"
I need to pass the fields in body inside the fetch.js file like below
const body = {
criteria,
fields: ["description", "placeLocation", "whosPlace"],
skip: 0,
take: 2000
};
And then I am trying to map the results like this. Please note that places is the result of the fetches call to API calling function.
let result = [];
if (places) {
result = places.results.map(p => ({
placeId: p.description, name: p.placeLocation
}));
}
the fetch function from fetch file look like below
export const fetchPlacesTo = async () => {
const criteria = [
{
anyOf: [
{
field: "WhosPlace",
value: "L%",
operator: "li"
}
]
}
];
const body = {
criteria,
fields: ["description", "placeLocation", "whosPlace"],
skip: 0,
take: 2000
};
const result = sessionManager
.refreshFetch(
`${**********_API_URI}/place/place`,
fetchUtils.hwsGetRequest(body)
)
.then(parseResponse);
return result;
};
And the function which calls this is below
export const getPlacesTo = () => async dispatch => {
try {
dispatch(loading.incrementLoading());
const places = await fetches.fetchPlacesTo().catch(error => {
console.log(error); //CRS: Change to logger
dispatch(loading.setWarning(errors.ERROR_GET_DATA));
});
let result = [];
if (places) {
result = places.results.map(p => ({
placeId: p.description, name: p.placeLocation
}));
}
dispatch({
type: constants.GET_LOOKUP_ENTITY,
payload: {
name: "placeIdToRepair",
value: result
}
});
I guess you want placeId and placeLocation froom response into result
var result=[]
var data={results: [{placeId: "BHLLC", placeLocation: "BUFR", locationType: "BUFR"},{placeId: "BHALC", placeLocation: "BAFR", locationType: "BAFR"},{placeId: "BHBLLC", placeLocation: "BUBFR", locationType: "BUBFR"}]}
data.results.map(p=>result.push({
placeId: p.placeId, name: p.placeLocation
}));
console.log(result)//(3) [{…}, {…}, {…}]
0:
placeId: "BHLLC"
name: "BUFR"
__proto__: Object
1:
placeId: "BHALC"
name: "BAFR"
__proto__: Object
2:
placeId: "BHBLLC"
name: "BUBFR"
__proto__: Object
length: 3

i can see props coming from action(redux) in console logs but can't access on window. Giving TypeError: Cannot read prop 'rest' of undefined

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.

Array prop returns Observer so can't access at [0]

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
}
})
},

react redux merge state with given array

Lets say I have a reducer which is like :
const initialState = [
{
accessToken: null,
isLoggedIn: false,
}
]
export default function my_reducer(state = initialState, action) {
switch (action.type) {
case LOGIN:
return state.merge(user: action) ---> how to handle this
and the output should be like:
[
{
accessToken: null,
isLoggedIn: false,
user: {
name: 'some name',
email: 'some email'
}
]
In action I am getting a array which I am providing by doing JSON.stringify(response)
previous data should not be changed and new data should be updated
The ES6 Way
To create a new object with the state in ES6 we can use the spread operator. Like this
...
case ActionType.SUCCESS_GET_DATA : {
let newState = { ...state, [action.uniqueKey]: action.payload };
return state.merge(newState);
}
...
I did the uniqueKey part as a variable because you will want a unique name for your state.
IMO this syntax is much easier to understand than the Object.assign
You can use Object.assign() function:
var state = {
accessToken: null,
isLoggedIn: false,
};
var user = {
name: 'some name',
email: 'some email'
};
var newState = Object.assign({}, state, {user});
console.log(newState);
First I see that your state is actually an array, but I think you would need an object right?
So it would be:
const initialState = {
accessToken: null,
isLoggedIn: false,
}
(requires Babel) So with spread operator you can:
return {
...initialState,
user: {
name: '...',
surname: '...'
}
};
Or if you do not transpile via Babel alike:
return Object.assign({}, initialState, {
user: {
name: '...',
surname: '...'
}
});
Using ES6 spread syntax
...
case 'ACTION_TYPE_A': {
return { ...state, action.key: action.value };
}
...
This will return the merged state by updating the 'key' if it exists in the original state.
Everything according to new Es6 format : )
A total addToDo reducer function where the data is appended to the previous state. And you get the output of a new state data : )
export const addToDo = (state, action) => {
const { name, email, phone, image,key } = action;
var data = [...state.data];
var newData = {
name: name, email: email, phone: phone, image: image,key:key
}
data.push(newData)
return(
state.merge({
data : data
})
)};
Happy coding.

Categories