I want to fetch data from API and trying to use in index.js
Error:
TypeError: Cannot read properties of undefined (reading 'icon')
When I console.log it prints
Empty Array of index.js > response data of weatherSlice.js > and finally response data of index.js as an array
I was getting undefined error and tried this and kinda worked .
{getCity.length !== 0 && (
<Typography variant="h6" component="div">
<div>Wind : {getCity.current.gust_kph} kph</div>
<div>Pressure: {getCity.current.pressure_in} in</div>
</Typography>
)}
But this time it gives same error on this block
{getCity.length !== 0 && (
<Grid item xs={3} sx={{mb:3}}>
<div className="label">{getDate()}</div>
<div className="label"> <img src={`${getCity.forecast.forecastday[1].condition.icon}`} alt="" /></div> // Gives undefined (reading"icon")
<div className="label" >22 C</div>
</Grid>)}
Index.js
const dispatch = useDispatch();
const [selectedCity, setSelectedCity] = useState('Ankara');
const getCity = useSelector((state) => state.weather.item);
const [datee , setDate ] = useState('');
useEffect(() => {
dispatch(fetchDefault(selectedCity))
setDate(getDate())
}, [dispatch])
weatherSlice
export const fetchDefault = createAsyncThunk('weather/getWeather', async (selectedCity) => {
const res = await axios(`http://api.weatherapi.com/v1/forecast.json?key=ebb6c0feefc646f6aa6124922211211&q=${selectedCity}&days=10&aqi=no&alerts=no
`)
return res.data});
export const weatherSlice = createSlice({
name: "weather",
initialState : {
item : [],
},
reducers:{},
extraReducers:{
[fetchDefault.fulfilled]: (state , action) => {
state.item = action.payload;
console.log(state.item)
},
[fetchDefault.pending]: (state , action) => {
console.log("sadsad")
}
}
After the state.weather.item state is populated it's now an object, not an array. The initial condition getCity.length !== 0 works/passes because getCity.length is undefined and undefined !== 0 evaluates true. The issue is occurring after you start accessing into the state.
The fetched city data is an object with location, current, and forecast properties.
// 20211114135700
// https://api.weatherapi.com/v1/forecast.json?key=ebb6c0feefc646f6aa6124922211211&q=seattle&days=10&aqi=no&alerts=no
{
"location": {
"name": "Seattle",
"region": "Washington",
"country": "United States of America",
"lat": 47.61,
"lon": -122.33,
"tz_id": "America/Los_Angeles",
"localtime_epoch": 1636927019,
"localtime": "2021-11-14 13:56"
},
"current": {
"last_updated_epoch": 1636926300,
"last_updated": "2021-11-14 13:45",
"temp_c": 16.1,
"temp_f": 61.0,
"is_day": 1,
"condition": {
"text": "Light rain",
"icon": "//cdn.weatherapi.com/weather/64x64/day/296.png",
"code": 1183
},
"wind_mph": 13.6,
"wind_kph": 22.0,
"wind_degree": 190,
"wind_dir": "S",
"pressure_mb": 1014.0,
"pressure_in": 29.94,
"precip_mm": 1.0,
"precip_in": 0.04,
"humidity": 90,
"cloud": 100,
"feelslike_c": 16.1,
"feelslike_f": 61.0,
"vis_km": 3.2,
"vis_miles": 1.0,
"uv": 4.0,
"gust_mph": 18.8,
"gust_kph": 30.2
},
"forecast": {
"forecastday": [
{
"date": "2021-11-14",
"date_epoch": 1636848000,
"day": {
"maxtemp_c": 16.2,
"maxtemp_f": 61.2,
"mintemp_c": 11.5,
"mintemp_f": 52.7,
"avgtemp_c": 14.9,
"avgtemp_f": 58.8,
"maxwind_mph": 16.1,
"maxwind_kph": 25.9,
"totalprecip_mm": 21.1,
"totalprecip_in": 0.83,
"avgvis_km": 9.3,
"avgvis_miles": 5.0,
"avghumidity": 93.0,
"daily_will_it_rain": 1,
"daily_chance_of_rain": 99,
"daily_will_it_snow": 0,
"daily_chance_of_snow": 0,
"condition": {
"text": "Heavy rain",
"icon": "//cdn.weatherapi.com/weather/64x64/day/308.png",
"code": 1195
},
"uv": 1.0
},
"astro": {
...
},
"hour": [
...
]
},
...
]
}
}
You are attempting to render the forecast, but here's where the code really goes sideways. You're assuming there's at least 2 elements (i.e. the getCity.forecast.forecastday.length>= 2), and then if there is, assume theres aconditionproperty. When there isn't andgetCity.forecast.forecastday[1].condition` is undefined, this is the error you are seeing.
From what I can tell, the condition property is nested within a day field. Since it's unclear what properties are guaranteed to exist in the response data your best bet is to:
First make sure you are accessing into the correct path
Use null-checks/guard-clauses or Optional Chaining operator to prevent accidental null/undefined accesses
The updated object property path is as follows:
getCity.forecast.forecastday.[1].day.condition.icon
If any of these are potentially undefined or otherwise not returned in the data, the corrected access using the Optional Chaining operator is as follows:
getCity.forecast?.forecastday?.[1]?.day?.condition?.icon
Which is the equivalent null-check/guard-clause version of:
getCity.forecast
&& getCity.forecast.forecastday
&& getCity.forecast.forecastday[1]
&& getCity.forecast.forecastday[1].day
&& getCity.forecast.forecastday[1].day.condition
&& getCity.forecast.forecastday[1].day.condition.icon
Use the same sort of check for the current weather data:
{getCity.current && (
<Typography variant="h6" component="div">
<div>Wind : {getCity.current.gust_kph} kph</div>
<div>Pressure: {getCity.current.pressure_in} in</div>
</Typography>
)}
Finally, update the initial state for the weather slice to match the data invariant, it should be an object.
initialState : {
item : {},
},
Related
I'm building a component that allows me to compare two objects. It accepts a list of fields to compare and a list of fields that need to be ignored in string format
Here is an example of the object that will be compared:
{
// (..... More elements above .....)
taskData: {
"uniqueId": "OrdenTrabajo48",
"id": 48,
"position": 1,
"name": "Dirección Obra Civil",
"description": "Dirección Obra Civil Afecta: Sorda, Roberto",
"startDate": "2021-10-16T11:00:00.000Z",
"endDate": "2022-06-01T11:00:00.000Z",
"duration": 227,
"progress": 73,
"hours": 0,
"realHours": 15,
"predecessor": null,
"child": [],
"resourceInfo": [
{
"uniqueId": "Persona_1MJ0VE9G0",
"id": "OrdenTrabajo48Persona_1MJ0VE9G0",
"name": "Sorda, Roberto",
"group": "Subgerencia de Planes y Proyectos - SUB_PLAN_PROY_SIT",
"unit": 4.1667,
"startDate": "2021-10-16T03:00:00.000+00:00",
"endDate": "2022-06-01T02:59:59.000+00:00",
"hours": 0,
"realHours": 15,
"avatar": "http://localhost:8091/images/llama.jpg"
}
],
"comments": null,
"etiquetas": [],
"baseLineStartDate": null,
"baseLineEndDate": null
}
// (..... More elements below .....)
}
(But to clarify, it could be any object. The component is abstract and can be used anywhere)
The component doesn't know the structure of the object to compare, just the object and the paths in string format
I want to remove in every element of the array resourceInfo, the properties avatar, icon, label and color regardless the length of the array, but I don't know if there is a syntax to do that.
Also I want to remove the property realHours
This is what I tried:
const ignoredFields = [
'taskData.resourceInfo[?].avatar', //<--- It's not working
'taskData.resourceInfo[].icon', //<--- Neither this
'taskData.resourceInfo.label', //<--- Or this
'taskData.resourceInfo[0].color', //<--- This hardcode is working, but I don't know the length in that scope
'taskData.realHours' // <-- No problems here
];
const currentComparableObject = _.omit(obj, ignoredFields);
const oldComparableObject = _.omit(prev, ignoredFields);
var fieldsToOmit=[];
var resourceInfoFields=['avatar','icon','label','color'];
var globalFields=['realHours'];
taskData.resourceInfo.forEach((item,index)=>{
resourceInfoFields.forEach((field)=>{
fieldsToOmit.push(`resourceInfo[${index}].${field}`)
})
})
console.log( _.omit(taskData, fieldsToOmit.concat(globalFields)))
You do not need lodash to delete fields from an array. I mean you can if you really want to but, it is trivial to loop through the array and delete the fields you want.
#Yasser CHENIK isn't wrong just doesn't do a good job of explaining the solution.
Below I have included a thorough example so you can test for yourself.
NOTE this solution mutates the original array but, it is not difficult to use this concept to make an immutable version.
const taskData = {
"uniqueId": "OrdenTrabajo48",
"id": 48,
"position": 1,
"name": "Dirección Obra Civil",
"description": "Dirección Obra Civil Afecta: Sorda, Roberto",
"startDate": "2021-10-16T11:00:00.000Z",
"endDate": "2022-06-01T11:00:00.000Z",
"duration": 227,
"progress": 73,
"hours": 0,
"realHours": 15,
"predecessor": null,
"child": [],
"resourceInfo": [
{
"uniqueId": "Persona_1MJ0VE9G0",
"id": "OrdenTrabajo48Persona_1MJ0VE9G0",
"name": "Sorda, Roberto",
"group": "Subgerencia de Planes y Proyectos - SUB_PLAN_PROY_SIT",
"unit": 4.1667,
"startDate": "2021-10-16T03:00:00.000+00:00",
"endDate": "2022-06-01T02:59:59.000+00:00",
"hours": 0,
"realHours": 15,
"avatar": "http://localhost:8091/images/llama.jpg"
},
{
"uniqueId": "Persona_1MJ0VE9G0",
"id": "OrdenTrabajo48Persona_1MJ0VE9G0",
"name": "Sorda, Roberto",
"group": "Subgerencia de Planes y Proyectos - SUB_PLAN_PROY_SIT",
"unit": 4.1667,
"startDate": "2021-10-16T03:00:00.000+00:00",
"endDate": "2022-06-01T02:59:59.000+00:00",
"hours": 0,
"realHours": 15,
"avatar": "http://localhost:8091/images/llama.jpg"
},
{
"uniqueId": "Persona_1MJ0VE9G0",
"id": "OrdenTrabajo48Persona_1MJ0VE9G0",
"name": "Sorda, Roberto",
"group": "Subgerencia de Planes y Proyectos - SUB_PLAN_PROY_SIT",
"unit": 4.1667,
"startDate": "2021-10-16T03:00:00.000+00:00",
"endDate": "2022-06-01T02:59:59.000+00:00",
"hours": 0,
"realHours": 15,
"avatar": "http://localhost:8091/images/llama.jpg"
},
],
"comments": null,
"etiquetas": [],
"baseLineStartDate": null,
"baseLineEndDate": null
}
const fieldsToOmit = [
'avatar',
'icon',
'label',
'color',
'realHours'
]
console.log(taskData.resourceInfo);
taskData.resourceInfo.forEach(info => {
fieldsToOmit.forEach(field => {
delete info[field];
})
});
console.log(taskData.resourceInfo);
You can remove properties in a functional manner (immutable) by using destructuring:
const {realHours, ...result} = {
...taskData,
resourceInfo: taskData.resourceInfo.map(
({avatar, icon, label, color, ...keep}) => keep
)
};
console.log(result);
Thanks for the answers to all.
To solve partially the problem, I created a function that does the following:
It filters the references that contains [?] (i.e: taskData.resourceInfo[?].avatar)
Then obtain the first part of the string (That is, the path to reach the array) and the second part (property name)
Using _.get from lodash it retrieves the length of the array and creates a new fieldReference with the index, so loadash can read it.
private sanitizeArrays(obj: any, fieldReferences: string[]): string[] {
const fieldsDup = [...fieldReferences];
// Get Elements that contains [?] in the property name
const arrays = fieldsDup.filter(ignoredField => ignoredField.match(/\[\?]/g));
// Remove elements that contain [?] from ignoredFieldsDuplicated
fieldsDup.forEach((ignoredField, index) => {
if (ignoredField.includes('[?]')) {
fieldsDup.splice(index, 1);
}
});
// Get the properties names without [?]
const arrayPropertyName = arrays.map(ignoredField => ignoredField.split('[')[0]);
const afterArrayPropertyName = arrays.map(ignoredField => ignoredField.split(']')[1]);
// For each array that I have...
arrayPropertyName.forEach((array, index) => {
const length = _.get(obj, array).length;
for (let i = 0; i < length; i++) {
fieldsDup.push(array + '[' + i + ']' + afterArrayPropertyName[index]);
}
});
return fieldsDup;
}
Example input (if the object contains only one element in resourceInfo):
'taskData.resourceInfo[?].avatar',
'taskData.resourceInfo[?].icon',
'taskData.resourceInfo[?].label',
'taskData.resourceInfo[?].color',
'taskData.resourceInfo[?].fontColor',
'taskData.realHours'
Example output:
taskData.resourceInfo[?].icon
taskData.resourceInfo[?].color
taskData.realHours
taskData.resourceInfo[0].avatar
taskData.resourceInfo[0].icon
taskData.resourceInfo[0].label
taskData.resourceInfo[0].color
taskData.resourceInfo[0].fontColor
(javascript includes() isn't playing nice deleting the [?])
Also it doesn't work for nested arrays...
I am trying to fetch API data but an error shows that data.map is not a function (in my case users.map). Please help me out.
I'm getting an error that map.data is undefined as a function? Looking at it I don't know what's not working .
import React, { useEffect, useState } from "react";
import "./bodypart.css";
const Bodypart = () => {
const [users, setUsers] = useState([]);
const getUsers = async () => {
const response = await fetch(
"https://www.flickr.com/services/rest/?method=flickr.photos.getRecent&api_key=XXXXXXXXXXXXXXXXXXXXX&format=json&nojsoncallback=1"
);
setUsers(await response.json());
};
useEffect(() => {
getUsers();
}, []);
return (
<div className="bodypart">
<div className="bodypart__image">
<div className="image">
{users.map((curElem) => {
return (
<img
src="https://www.thisislocallondon.co.uk/resources/images/12088462.jpg?display=1&htype=0&type=responsive-gallery"
classname="bodypart__image"
alt=""
/>
);
})}
</div>
</div>
</div>
);
};
export default Bodypart;
API DATA is given below. I am unable to solve the problem by myself.
{
"photos": {
"page": 1,
"pages": 10,
"perpage": 100,
"total": 1000,
"photo": [
{
"id": "51398681312",
"owner": "193303965#N08",
"secret": "594df2ff71",
"server": "65535",
"farm": 66,
"title": "",
"ispublic": 1,
"isfriend": 0,
"isfamily": 0
},
{
"id": "51398681362",
"owner": "193209554#N03",
"secret": "2de06284c5",
"server": "65535",
"farm": 66,
"title": "SPIDERMAN NO WAY HOME RETOUCHES",
"ispublic": 1,
"isfriend": 0,
"isfamily": 0
},
{
"id": "51398682272",
"owner": "193683913#N05",
"secret": "d8258fa802",
"server": "65535",
"farm": 66,
"title": "Türkiyədə FACİƏ: Sosial şəbəkə üçün video çəkən qız yüksəklikdən yerə çırpıldı - ANBAAN VİDEO",
"ispublic": 1,
"isfriend": 0,
"isfamily": 0
},
]
}
EDIT: Hello Friends , I got the answer instead of
{users.map((curElem) => {
we will have to write
{users.photos.photo.map((curElem) => {
EDIT 2
What should I write inside my console statement to return id of photo
{
"photos": {
"page": 1,
"pages": 10,
"perpage": 100,
"total": 1000,
"photo": [
{
"id": "51400489782",
"owner": "144450638#N02",
"secret": "2e68b6bb36",
"server": "65535",
"farm": 66,
"title": "riki-shaham-wong-ping",
"ispublic": 1,
"isfriend": 0,
"isfamily": 0
},
Clearly your API call is returning undefined or something of that nature, which sets the users to undefined. Then when you use in later, you can't call map on undefined, which is the error you are getting.
You must first verify why that's happening and put some defaults so you don't run into run time exceptions.
setUsers((await response.json()) ?? []);
Check your fetch part inside getUsers. I copied the API address and got this error.
{"stat":"fail","code":100,"message":"Invalid API Key (Key has invalid format)"}
You can update your funtion as follow.
const response = await fetch(
...url
);
const data = response.json()
if(Array.isArray(data))
setUsers(data);
Try putting a console.log(users) somewhere in that component to see what fetch is returning from flickr. It may be an object that you need to extract an array property from.
Then check the browser developer tools console. You should see two console outputs before the type error; an empty array from the first rendering [] and the response from the fetch call during the second rendering.
const [users, setUsers] = useState([]);
console.log('Users: ', users);
I have a problem looping on object. I couldn't solve it. Please help me.
I am on a React project to improve my skill and gonna work with React Hooks.
There is a file like below which contains an object. I'm gonna fetch from an api.
I wanna to display the object details which i need on the page.
I've tried a lot of way to solve but they didn't work.
I am not adding all the codes what i tried but there is the simple one below which tells my goal. What is the correct solution to iterate on objects in React, JS ..
Thanks in advance.
import React from 'react';
export default function ValueRef() {
const myList = {
"location_suggestions": [
{
"id": 61,
"name": "London",
"country_id": 215,
"country_name": "United Kingdom",
"country_flag_url": "https://b.zmtcdn.com/images/countries/flags/country_215.png",
"should_experiment_with": 0,
"has_go_out_tab": 0,
"discovery_enabled": 0,
"has_new_ad_format": 0,
"is_state": 0,
"state_id": 142,
"state_name": "England and Wales",
"state_code": "England and Wales"
},
{
"id": 3454,
"name": "London, ON",
"country_id": 37,
"country_name": "Canada",
"country_flag_url": "https://b.zmtcdn.com/images/countries/flags/country_37.png",
"should_experiment_with": 0,
"has_go_out_tab": 0,
"discovery_enabled": 0,
"has_new_ad_format": 0,
"is_state": 0,
"state_id": 124,
"state_name": "Ontario",
"state_code": "ON"
},
{
"id": 5836,
"name": "London, KY",
"country_id": 216,
"country_name": "United States",
"country_flag_url": "https://b.zmtcdn.com/images/countries/flags/country_216.png",
"should_experiment_with": 0,
"has_go_out_tab": 0,
"discovery_enabled": 0,
"has_new_ad_format": 0,
"is_state": 0,
"state_id": 85,
"state_name": "Kentucky",
"state_code": "KY"
},
],
"status": "success",
"has_more": 0,
"has_total": 0,
"user_has_addresses": true
}
console.log(myList)
return (
<div>{
Object.values(myList).map((detail)=>(<div>{detail}</div>))
}</div>
)
}
Error: Objects are not valid as a React child (found: object with keys {id, name, country_id,
country_name, country_flag_url, should_experiment_with, has_go_out_tab, discovery_enabled,
has_new_ad_format, is_state, state_id, state_name, state_code}). If you meant to render a collection
of children, use an array instead.
This is because you are iterating over nested objects, that needs to be treated differently.
Oversimplifying your object, it is something like this :
const myList = {
"location_suggestions": [ ...objects ],
"status": "success",
"has_more": 0,
"has_total": 0,
"user_has_addresses": true
}
Now, when you do :
Object.values(myList).map((detail)=>(<div>{detail}</div>))
As you can see in your first iteration itself, the detail object contains an array, which is a type of object, that cannot be kept in a React render return.
There are 2 solutions to your problem,
Remove / skip the location_suggestions key to avoid the error.
Create a seperate iteration logic for location_suggestions
I'm trying to access data further down into my JSON file. I am able to easily access data in the first two data sets in rows and area:
data.json
"rows": [
{
"uid":"001",
"type": "Lorem ipsum",
"area": [
{
"name": "London",
"number": "12345",
"wait": {
"start": {
"start_time": 1585129140,
"delay": 300
},
"end": {
"end_time": 1585130100,
"delay": 300
}
},
"in": 1585129140,
"out": 1585130100,
},
However when I try to access the data under wait which includes this block:
"wait": {
"start": {
"start_time": 1585129140,
"delay": 300
},
"end": {
"end_time": 1585130100,
"delay": 300
}
},
No data is getting returned on screen from my jsx file, but it is available in the console log
TimeTracker.jsx
const TimeTracker = (props) => {
const trainTime = useState(props.data);
console.log(props.data);
return (
<>
<div className={style.timeLabel}>
<div className={style.startTime}>{trainTime.start_time}</div>
<div className={style.endTime}></div>
</div>
</>
)
};
export default TimeTracker;
console.log
wait:
start:
start_time: 1585129140
delay: 300
__proto__: Object
end:
end_time: 1585130100
delay: 300
__proto__: Object
__proto__: Object
I've used the same pattern for passing props in other components and it works fine on the first two levels so I don't understand why it's not working. How do I get data from further in this JSON?
useState returns a tuple with the object and a function to set the value on the object. You probably need to change your component to something like this:
const TimeTracker = (props) => {
const [trainTime, setTrainTime] = useState(props.data);
console.log(props.data);
return (
<>
<div className={style.timeLabel}>
<div className={style.startTime}>{trainTime.start_time}</div>
<div className={style.endTime}></div>
</div>
</>
)
};
export default TimeTracker;
A nested property can not be accessed by one level of a.b so instead of
<div className={style.startTime}>{trainTime.start_time}</div>
it should be
<div className={style.startTime}>{trainTime.wait.start.start_time}</div>
I fetch an api on componentDIdMount() then store the json to a state then I pass that state as a prop, I have no problem showing the data except on arrays.
<Component details={this.state.details} />
json:
{
"adult": false,
"backdrop_path": "/qonBhlm0UjuKX2sH7e73pnG0454.jpg",
"belongs_to_collection": null,
"budget": 90000000,
"genres": [
{
"id": 28,
"name": "Action"
},
{
"id": 878,
"name": "Science Fiction"
},
{
"id": 35,
"name": "Comedy"
},
{
"id": 10751,
"name": "Family"
}
]
}
then I try to map the genres:
<div className={style.genre}>
{details.genres.map(g => (
<span key={g.id}>{g.name}</span>
))}
</div>
But then I get Cannot read property 'map' of undefined, I don't know why this is happening because I'm able to do details.budget
It's trying to read data before you get the result from api.
so write the map function as
{details&&details.genres&&details.genres.map(g => (
<span key={g.id}>{g.name}</span>
))}
In react Initially when component is mounted, render() function is called and then componenentDidMount() is called in which you fetch data. So Initially details is empty. So you need to write the condition.