.map is not a function - react - javascript

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

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

Why is one API response returning just an object over and array of objects? [duplicate]

This question already has answers here:
Why is "forEach not a function" for this object?
(4 answers)
Closed 1 year ago.
Currently my Api is returning an object instead of an object within an array, however I can't seem to tell the difference between these two routes and why one would return said array of data over the other.
For instance :
router.get('/caseDetail/:id', (req,res) => {
db.caseDetail.findOne({
include : [db.Part,db.Fault,db.Disposition,db.Contact,db.Site]
}).then((response) => {
res.json(response);
}).catch((error) => {
console.table([stack.error,stack.id,error])
})
})
The above route returns an array of data while the following returns just an object
router.get('/caseDetail/:caseName', (req,res) => {
db.caseDetail.findAll({
include : [db.Part,db.Fault,db.Disposition,db.Contact,db.Site],
where : {
caseName:{
[Op.like]: req.params.caseName}
}
}).then((response) => {
console.log(response);
res.json(response)
}).catch((error) => {
console.log(error);
})
})
-------------------------- For context----------------------------
I've enacted this method multiple times, even in other script files, but I haven't been required to parse data in this manner, is something out of shape here that im over looking? Am I missing a JSON.parse(); here? Github
try {
const items = await axios.get(`/api/caseDetail/:caseName` + caseName);
console.log(items.data);
$tbody.empty()
items.data.forEach((item) => {
console.log(item);
Returned Results
{id: 2, caseName: "1 - Fenway Park - 1", createdAt: "2021-07-27T18:13:55.000Z", updatedAt: "2021-07-27T18:13:55.000Z", UserId: 1, …}
Error Message
TypeError: items.data.forEach is not a function
at callSearchResults (searchInventory.js:29)
If I understand you correctly and this is the result that you receive from your request:
{id: 2, caseName: "1 - Fenway Park - 1", createdAt: "2021-07-27T18:13:55.000Z", updatedAt: "2021-07-27T18:13:55.000Z", UserId: 1, …}
Then you would need to take the entries out of it and iterate over them like so:
for (const [key, value] of Object.entries(items)) {
console.log(`${key}: ${value}`);
}
Or like this:
Object.entries(items).forEach(([key, val]) => console.log(key, val));
forEach() is designed for arrays but you can typecast collections and objects into arrays like this...
var I=[...items];
var D=[...items.data]
Now you should be OK, try...
I.data.forEach((item) => {}
D.forEach(() => {}

Turn a for loop into a foreach loop not working

I am trying to turn a for loop into a forEach loop, but it doesn't seem to be working...
Here is my code:
const townDataURL = "[some link I probably can't disclose...]"
const towns2get = [
"Preston",
"Fish Haven",
"Soda Springs"
]
fetch(townDataURL)
.then((response) => {
return response.json()
})
.then((jsonData) => {
const towns = jsonData["towns"].filter((item) => {
// for (let i = 0; i<towns2get.length; i++) {
// if (item.name == towns2get[i]) {
// return item
// }
// }
return (towns2get.forEach(elem => {
return ( (item.name == elem) ? (item) : "Hello?" )
}))
})
console.log(towns)
})
When I have the commented code run it gives me this:
(3) [{…}, {…}, {…}]
0: {name: "Fish Haven", photo: "fishhaven.jpg", motto: "This is Fish Heaven.", yearFounded: 1864, currentPopulation: 501, …}
1: {name: "Preston", photo: "preston.jpg", motto: "Home of Napoleon Dynamite.", yearFounded: 1866, currentPopulation: 5204, …}
2: {name: "Soda Springs", photo: "sodasprings.jpg", motto: "Historic Oregon Trail Oasis. The Soda is on Us.", yearFounded: 1858, currentPopulation: 2985, …}
length: 3
__proto__: Array(0)
Which is exactly what I want, but I want to simplify my code... what I have now gives me this:
[]
length: 0
__proto__: Array(0)
I've done some debugging and I know that my conditional statement with the ternary operator works fine, and it is returning a value... but I can't seem to figure out why it isn't returning it back to the filter method...
Does it not work this way? Or do I have to somehow put the forEach with the filter?
Thank you for any help!
The better way here is to use the .includes function inside the filter method
const townDataURL = "[some link I probably can't disclose...]"
const towns2get = [
"Preston",
"Fish Haven",
"Soda Springs"
]
fetch(townDataURL)
.then((response) => {
return response.json()
})
.then((jsonData) => {
const towns = jsonData["towns"].filter((item) => {
if(towns2get.includes(item.name) > -1) return true;
else return false;
})
console.log(towns)
})
Cris G gave me the answer:
.forEach() doesn't return anything, so your .filter() callback returns undefined, a falsey value, for each element. Use filter(item => towns2get.includes(item.name)) – Chris G
So the code should then be:
const townDataURL = "[some link I probably can't disclose...]"
const towns2get = [
"Preston",
"Fish Haven",
"Soda Springs"
]
fetch(townDataURL)
.then((response) => {
return response.json()
})
.then((jsonData) => {
const towns = jsonData["towns"].filter(item => towns2get.includes(item.name))
console.log(towns)
})

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

Categories