Add several nested JSON properties in a component in React - javascript

I am learning React and have a API request to OpenWeather that results in something like this:
{
coord: {
lon: -0.13,
lat: 51.51
},
weather: [{
id: 500,
main: "Rain",
description: "light rain",
icon: "10d"
}],
base: "stations",
main: {
temp: 293.21,
pressure: 1011,
humidity: 86,
temp_min: 292.45,
temp_max: 294.26
},
wind: {
speed: 1.54,
deg: 243,
gust: 3.08
},
rain: {
3h: 0.78
},
clouds: {
all: 56
},
dt: 1466431324,
sys: {
type: 3,
id: 10115,
message: 0.0463,
country: "GB",
sunrise: 1466394185,
sunset: 1466454093
},
id: 2643743,
name: "London",
cod: 200
}
I'm having problems using several parts of this JSON data.
I can call the result and first layer without problems, but calling any of the nested properties responds in an error saying it can't read the property of undefined.
I guess this is because the state is not defined, does it need to be defined for all the nested layers? I have defined the root, like this:
var React = require('react');
var Httprequest = require('../services/weatherservice.js');
var CurrentWeather = require('./CurrentWeather.jsx');
var WeatherApp = React.createClass({
getInitialState: function() {
return {currentWeather: '', apiCall: ''};
},
componentWillMount: function() {
Httprequest.get('London')
.then(function(data) {
this.setState({apiCall: data});
this.setState({currentWeather: data.weather[0]});
}.bind(this));
},
render: function() {
var Res = this.state.apiCall;
return (
<div>
<h1>Weather App</h1>
<CurrentWeather weatherNow={Res.main.humidity} />
</div>
);
}
});
module.exports = Weather;
Perhaps I shouldn't use state? I'm thinking of creating a solution where the user can change the city later on that's why I'm using state. I would appreciate it a lot if someone experienced could explain and perhaps refactor my example using a good practice so that I can learn doing it right.

Let me try to explain what you have done)
1) before first render you send request which will return data, at state you have {currentWeather: '', apiCall: ''}
2) request is async, so
.then(function(data) {
this.setState({apiCall: data});
this.setState({currentWeather: data.weather[0]});
}.bind(this))
this code will be called after several seconds
3) render method called, in which you doing something like this:
this.state.apiCall.main.humidity
but, for now in state you still have {currentWeather: '', apiCall: ''} so we will have error
how to solve this? simple just add some checks like
weatherNow={Res && Res.main && Res.main.humidity}
i also recommend you to read react styleguids, for now your component look ugly)

Related

How can I modify and extend properties of an object located in useState Array?

I'm a react.js beginner, searching for methods to alter my data structure. For example, I want to push new objects into the children-array or remove them by key.
What is the appropriate way to do that?
const [treeData, setTreeData] = useState([
{
title: "parent 1",
key: "0-0",
icon: <UserAddOutlined />,
children: [
{
title: "parent 1-0",
key: "0-0-0",
icon: <UserAddOutlined />,
children: [
{
title: "leaf",
key: "0-0-0-0",
icon: <UserAddOutlined />,
},
{
title: "leaf",
key: "0-0-0-1",
icon: <UserAddOutlined />,
},
],
},
{
title: "parent 1-1",
key: "0-0-1",
icon: <UserAddOutlined />,
children: [
{
title: "sss",
key: "0-0-1-0",
icon: <UserAddOutlined />,
},
],
},
],
},
]);
So you should not update the state directly. It is not allowed.
Maybe where you are receiving data from, suppose via api and the data is response.payload.data etc.
So in your case use the setTreeData(response.payload.data) method to add stuff in it.
Now if you want to update certain value (remove or update using index etc). Obviously you will have to have index somehow.
So for deleting say you will have some click and against that a handler for it
removeItem(e) {
item_to_remove = e.target..... etc // to get the item's reference for matching
setTreeData(treeData.filter(items => item.<someproperty> != item_to_remove))
// In your case could also be targetting children maybe
// setTreeData(treeData.Children.filter(items => item.<someproperty> != item_to_remove))
}
I would say maybe handle childrens' array inside another useState variable (childrenTreeData maybe). But you will have to look it's feasibility too. Just an idea after seeing your data
JUST for INFO
This is something similar I did for updating prices inside each cards in my project
const getCurrentPrice = useCallback(() => { // <======= maybe you do not need this
const updatedTilesData = tilesData.map((tile: any) => {
return {
...tile, // <======= get everything here and then update the price below for item
currentPrice: calculateDNCurrentPrice(
tile.startingPrice,
tile.dnTimestamp
),
};
});
setTilesData(updatedTilesData);
}, [tilesData]);

How to pass a sectionlist sectionHeader's index to the reducer?

Looking to map items with index using SectionList, I was successfully able to map the index of the sections and pass the state to the reducer, however, the section header has a different index and would like to do the same with it. Help me up with some examples if possible. This is something that I am developing for a food tech app which and I want to implement the Cart Functionality in the App.
SectionList:
<SectionList
sections={this.props.user.dailyMenuData.item.map(
({category, data}, index2) => ({category, data, index2}),
)}
keyExtractor={this.keyExtractor}
renderItem={this.renderMenu}
renderSectionHeader={({section: {category, index2}}) => (
<CText style={styles.headerStyle}>{category}</CText>
)}
/>
The above data is coming from MongoDB like below:
DATA = {
vendor_id: XXXXXX (For Mapping Vendor to the Menu)
company_id: XXXXX (For Mapping the Customer to the Menu)
item: [
{
category: 'A La Carte',
data: [
{
src:
'https://cdn.pixabay.com/photo/2017/08/01/14/22/fruits-2565817_960_720.jpg',
name: 'Coffee with Pancakes',
price: '160',
rating: 3.8,
calorie: '130',
},
}],
},
The AddtoCart Reducer is dynamically getting the value of the index of data[] however it is not able to get the index of item[] from the API called. The reducer code is given below:
case ADDTOCART:
return update(state, {
dailyMenuData: {
item: {
1: {
data: {
[payload.index]: {
quantity: {
$set:
state.dailyMenuData.item[1].data[payload.index].quantity +
1,
},
},
},
},
},
},
})

Openweathermap API call responding with wrong json

I am using the hourly forecast API call from Openweathe map to build a weather app. I can correctly fetch the URL for the call but the structure of the JSON that shows seems not to match the one provided as an example. I can't access data.list or either data.sys or data.city.name. While I can access data.name.
Here is the example JSON
{
cod: "200",
message: 0.0208,
cnt: 96,
list: [
{
dt: 1553709600,
main: {
temp: 286.44,
temp_min: 286.258,
temp_max: 286.44,
pressure: 1015.82,
sea_level: 1015.82,
grnd_level: 1002.193,
humidity: 100,
temp_kf: 0.18
},
weather: [
{
id: 500,
main: "Rain",
description: "light rain",
icon: "10d"
}
],
clouds: {
all: 86
},
wind: {
speed: 5.51,
deg: 202.816
},
rain: {
1h: 0.812
},
sys: {
pod: "d"
},
dt_txt: "2019-03-27 18:00:00"
},
...
],
city: {
id: 420006353,
name: "Mountain View",
coord: {
lat: 37.3894,
lon: -122.0833
},
country: "US"
}
}
and here is the code I use to manage the call
function call(city,country,zip) {
let countryCode = list.getCode(country);
let API;
if (city!="" && country!="") {
API = 'http://api.openweathermap.org/data/2.5/weather?q='+city.toLowerCase()+','+countryCode.toLowerCase()+'&APPID=1a9b84b61d1a8d6fdbb52fa2800ef894';
}
else if(country!="" && zip!=""){
API ='http://api.openweathermap.org/data/2.5/forecast/hourly?zip='+zip.toString()+','+countryCode.toLowerCase()+'&APPID=1a9b84b61d1a8d6fdbb52fa2800ef894';
}
else{
alert("Please type your city or Zip code");
}
return API;
}
function getPlace() {
let city = cityInput.value;
let country = countryInput.value;
let zip = zipInput.value;
let APICall=call(city,country,zip);
fetch(APICall).then(response=>{
return response.json();
}).then(data=>{
let cityName= data.name;
settler(cityName);
})
}
I should be able to access data.list but
alert(data.list.lenght) gives me an error saying that can not define length of undefined. Same for alert(data.city.name) that says that can not access name property of undefined.
The weather API docs correctly show the data you should expect for weather query:
https://openweathermap.org/current
Your hourly query should work if you hit the correct endpoint (pro) which your (exposed; you should fix that) API key is not valid for (AFAICT).
The pricing page indicates that hourly data is not available for free.

Changing Vue Data Object

UPDATED CODE
<template>
<swiper-slide v-for="(photon,key) in $store.state.photons" :key='key'>
<radial-gauge :value='photon.tempF'
:options="photon.GaugeOptions.optionsLastTempF"></radial-gauge>
</swiper-slide>
</template>
store.js
export const store = new Vuex.Store({
state: {
photons: {},
token: '',
},
mutations: {
setUser(state, value) {
value.data.forEach(element => {
state.photons[element.id].user = element.data
state.photons[element.id].GaugeOptions.optionsLastTempF.highlights[0].to=parseInt(element.data.tempAlert,10)
state.photons[element.id].GaugeOptions.optionsLastTempF.highlights[1].from=parseInt(element.data.tempAlert,10)+1
});
}},
actions: {
async getData(context) {
let db = []
let photon = {}
db = await axios.get('http://13.14.13.1:3300/DB')
db.data.forEach(item => {
if ([Object.keys(item)[0]] in photon) {
let dataString = (Object.values(item)[0]).split(',')
photon[Object.keys(item)[0]].GaugeOptions={}
photon[Object.keys(item)[0]].GaugeOptions.optionsLastTempF={
title: 'Last Temp',
units: "°F",
minorTicks: 10,
majorTicks: ['0', '20', '40', '60', '80', '100', '120', '140', '160'],
maxValue: 160,
highlights: [{
"from": 0,
"to": 0,
"color": "rgba(0,255,0)"
},
{
"from":0,
"to": 160,
"color": "rgba(255,0,0)"
},
],
colorPlate: '#222',
colorUnits: '#ccc',
colorTitle: '#eee',
colorNumbers: '#eee',
width: 200,
height: 200
}
context.commit('setData', photon)
}
}
}
This code currently works if I go from the homepage to my chart page where this gauges are, but if i refresh the page the gauges have the right values but they aren't being properly update.
UPDATE
so after adding a v-if to my component it now loads properly with everything. I'm now having trouble with how I would call an update function on the gauge?If i'm doing the mutation in my vuex store how would i reference my component to call an update?
So, typically, Vuejs does not watch nested objects. I'd recommend that you rework you code a bit to avoid this. This should help: Vue.js - How to properly watch for nested data
Additionally, it looks like you are trying to reference the Vuex Store object, but you are using the data method rather than the computed method, so you won't get updates on that.

How to fiddle an easy OnDemandList (dgrid)?

I have tried to create a very simple jsfiddle to test an OnDemandList (sitepen/dgrid). But it does not render any rows. Does anybody have an idea what I have done wrong? Is it that a simple dstore/Memory does not offer methods like fetchRange? The jsFiddle can be found here: http://jsfiddle.net/rbeqqr2g/25/
require({
packages: [
{
name: 'dgrid',
location: '//cdn.rawgit.com/SitePen/dgrid/v0.3.16'
},
{
name: 'xstyle',
location: '//cdn.rawgit.com/kriszyp/xstyle/v0.2.1'
},
{
name: 'put-selector',
location: '//cdn.rawgit.com/kriszyp/put-selector/v0.3.5'
},
{
name: 'dstore',
location: '//cdn.rawgit.com/SitePen/dstore/master'
}
]
}, [
'dgrid/OnDemandList',
'dstore/Memory',
'dojo/dom',
], function(OnDemandList, Memory, dom) {
var data = [
{ id: 1, name: 'Peter' },
{ id: 2, name: 'Paul' },
{ id: 3, name: 'Mary' }
];
var store = new Memory({
data: data
});
var list = new OnDemandList({
collection: store,
minRowsPerPage: 5,
noDataMessage: "Keine Daten vorhanden",
renderRow: function (object, options) {
console.log("Zeile wurde gerendert.")
var div = document.createElement('div');
div.appendChild(document.createTextNode(object.name));
return div;
}
}, dom.byId('list'));
list.startup();
});
You're using the most recent dstore, but an old dgrid. The < 1.x versions of dgrid did not support dstore, you can use a regular dojo/store/Memory instead. In dgrid < 1.x, you also needed a store property instead of collection.
require({
...
}, [
'dgrid/OnDemandList',
//'dstore/Memory',
'dojo/store/Memory', // < --- regular dojo/store
'dojo/dom',
], function(OnDemandList, Memory, dom) {
...
...
var list = new OnDemandList({
//collection: store,
store: store, // <--- store property
...
}, dom.byId('list'));
list.startup();
});
http://jsfiddle.net/rbeqqr2g/28/
Alternatively, unless you're stuck with dgrid 0.3.x, you can also simply use the modern dgrid:
{
name: 'dgrid',
location: '//cdn.rawgit.com/SitePen/dgrid/v1.1.0'
//location: '//cdn.rawgit.com/SitePen/dgrid/v0.3.16'
},

Categories