OpenWeatherMap API: cannot read property of undefined - javascript

I am trying to access the weather information from an API call to OpenWeatherMap, but I always get a "cannot read property of undefined" error.
In my App.js file I have a CallAPI function that gets passed the users coordinates and fetches the weather data. It's then passed down to my Header component with a prop of "curr".
const [currWeatherData, setCurrWeatherData] = useState({})
useEffect(() => {
navigator.geolocation.getCurrentPosition(CallAPI, showError);
}, [])
//... showError function
function CallAPI(position){
const lat = position.coords.latitude
const long = position.coords.longitude
fetch(/*api call*/)
.then(response => response.json())
.then(data => {
setCurrWeatherData(data.current)
})
}
return (
<div>
<Header curr = {currWeatherData}/>
<div>
)
In my Header.js file I am currently just trying to display the weather status.
import React from "react"
function Header(props){
return(
<div>
{/*<h1>{props.curr.weather[0].main}</h1>*/}
{console.log(props.curr.weather)}
</div>
)
}
The beginning of the json file from the API looks like this.
json file
in "current", there is a weather property that is an array with one element which is an object, thus I would assume the correct way to access the "main" property would be "current.weather[0].main". However, I get the error "cannot read property '0' of undefined" when I try to console.log or return that. The strange part is that when I console.log "current.weather" it prints an array with an object to the console.
console
I've tried storing "current.weather" in a variable before accessing its 0th index and I've tried passing "currWeatherData.weather" as the prop in my App.js file, both of which I don't think change anything. I'm not really sure where to go from here, can anyone help?
EDIT: after an hour or so of console.log debugging i figured out my problem. I learned that when using hooks, useState triggers rerenders the same way this.setState did, meaning each time I set the state, it rendered my Header component. I'm guessing the API call didn't finish before rendering it, so the prop was passed as undefined. I solved this by adding an isLoading state and setting it to false after the API call,
//... code above
.then(data => {
setCurrWeatherData(data.current)
setIsLoading(false)
})
and in my return, I added a conditional statement
<div>
{!isLoading && <Header curr = {currWeatherData}/>}
</div>
I skimmed through https://medium.com/swlh/how-does-react-hooks-re-renders-a-function-component-cc9b531ae7f0 to help

In your App.js you have the following code:
return (
<div>
<Header curr = {currWeatherData}/>
<div>
However, this does not take the async API call into account. Therefore currWeatherData is null because the API call has not yet completed.
You need to consider the lifecycle and only try to render the data after the API call completes. There are various ways in React to do this, depending on your overall app/component design.
There's an example here based on componentDidMount.

since you are using a functional components. I suggest you add useEffact(()=> { your api call}, [] ) and this should work. because currently your code is not getting the data you want! hopefully this helps.

Related

React returns undefined and later the data with fetch api

With the below code I would like to use react-select but when I console.log(testUsers) at first this is blank and then data is finally there, but in the select data is blank. Is there any way to not select blank?
My code:
const { request: getUser, isLoading } = useRequest("");
const [testUsers, setUsers] = useState("");
useEffect(() => {
getUser({
path: `${someapi}/user?id=${record?.user_uid}`,
overwritePath: true,
}).then((data: any) => {
setUsers(data[0].fullname);
});
}, [testUsers]);
console.log(testUsers, "/////////////////");
The output of the console:
/////////
////////
////////
some api returns /////////////
It's to know that React runs the callback of an useEffect after all others normal JavaScript codes such as a console.log() and after the JSX is rendered. And even if that wasn't the case, a network request is asynchronous so you get the data after some delay.
The workaround here is to use a conditional rendering. Something like this as an exmple:
{!testUsers ? <p>Loading...</p> : <div>Render actual content</div> }
But the main error you are making here is to add testUsers in the dependency array. Since you are calling a state setter that's muting it you would get an infinite calls. Do like this instead:
useEffect(() => {
getUser({
path: `${someapi}/user?id=${record?.user_uid}`,
overwritePath: true,
}).then((data: any) => {
setUsers(data[0].fullname);
});
}, []);
Lastly, about why you are getting multiple console.log(), you can check this thread to get a detailed answer.
The reason for this behavior is React Life Cycle. what happened is that in the Mounting phases.
constructor()
getDerivedStateFromProps()
render()
componentDidMount().
because render is triggering first you see that blank select in your DOM.
Read more about it: https://www.w3schools.com/react/react_lifecycle.asp.
Solution:
in your JSX you can check if you have a value in your state then render your JSX.
{testUsers && <h1>Show</h1>}

I try to display api data but nothing comes out, not even an error on React

I'm slowly understanding the services and fetch with React but when I try to show something, it shows me absolutely nothing. I put the code in case someone can help me. Maybe I have to look at how to work with JSON, I don't know.
let datosApi = [];
const recogerDatos = () => {
let json = "https://jsonplaceholder.typicode.com/albums";
let miApi = "http://localhost/dsw/api/";
fetch(json)
.then(data => data.json())
.then(info => {
console.log(info);
this.datosApi = info;
})
}
function Services() {
return (
<>
{datosApi.map(datos => (
<p>{datos.title}</p>
))}
</>
);
}
export default Services;
JSON data appears in https://jsonplaceholder.typicode.com/albums
I think your example is missing something or you've not done it.
Basically there's a few things wrong:
recogerDatos is never being called
datosApi is not declared, and even if it was, it's not stateful, thus won't cause a re-render of your items.
I've created a working sandbox here that shows it working, and the code is below:
const [result, setResult] = useState([]);
const recogerDatos = () => {
let json = "https://jsonplaceholder.typicode.com/albums";
fetch(json)
.then((data) => data.json())
.then((info) => {
console.log(info);
setResult(info);
});
};
useEffect(() => {
recogerDatos();
}, []);
return (
<div className="App">
{result.length > 0 && result.map((datos) => <p>{datos.title}</p>)}
</div>
);
The recogerDatos function is called on page load (see useEffect), and the result is updated when the fetch is successful. This causes a re-render and the items are shown.
You are displaying data from your list
let datosApi = [];
However it does not seem like you are populating your list with data from the API since the method recogerDatos() is never being called.
From your code it seems like you're missing some core React patterns like state management and the components lifecycle. Since React re-renders components a lot you want to store things like fetched data into state to avoid them constantly reset to their initial value. And when you want to fetch the data you usually don't want to do it at each re-render (that would cause A LOT of fetching), instead you usually want to trigger it based on different events, for example when component (that will be used to show this data) is mounted. Such things are usually using the components lifecycle methods / useEffect hooks to ensure that they happen at the right point in time. I recommend you to go into React documentation and study the basics a bit more so you can understand the main concepts when you're coding, but here's a quick sandbox with an example of how you could get the desired effect in React:
https://codesandbox.io/s/beautiful-frost-on9wmn?file=/src/App.js

Map rendering React components with API data; "undefined" error

I am continuously running into this error with my first non-trivial React project. The error specifically is "TypeError: Cannot read property 'map' of undefined." The reason this error is happening is because React seems to load every component twice while I'm testing it.
Here's what I mean. I am trying to create a list of song names from a JSON I am receiving from the Spotify API. Each time the app loads I need to authenticate with Spotify before I can receive the data. I believe this is causing the undefined error as React is loading the component with the map function before there is data to map over.
import React from 'react';
function List(props) {
const userData = props.userData;
const favorites = Object.values(userData)[0]; // turns JSON object into usable array
// console.log(favorites)
// try {
// favorites.map( (item) => console.log(item.name));
// } catch {
// console.log("No favorites");
// }
return (
<div>
<p>Your favorite songs are:</p>
<ul>
{favorites.map( (item) => (<li key={item.id}>{item.name}</li>))}
</ul>
</div>
);
}
export default List;
If I uncomment out the try/catch statement and scrap the return statement, I get a console output that
looks like this.
So my question is how/why is this happening? What is the best way to map over data you don't have yet?
You have a couple of options, people normally either specify a default value or do only the processing when the value is defined, i prefer option 1, for example:
const favorites = Object.values(userData)[0] || []

invalid hook call on data retrieved by axios

I'm trying to retrieve the JSON data from here and then update the state in a functional component. Even though the code seems fine I'm getting an error saying its an invalid hook call.
On the react documentation it said that I might have 2 different react apps in the same folder however I checked it with the command they gave and there was only 1. However I am running this from a django server and there is a different react app in a different django app (so in a completely different folder).
const App = () => {
const [posts, setPosts] = useState([]);
useEffect(() => {
let url = "https://jsonplaceholder.typicode.com/posts";
axios.get(url).then(res => {
console.log(res.data);
// The code crashes here saying that it is an invalid hook call
useState(res.data);
}).catch(err => console.log(err));
}, []);
return (
<div>
This is just a place holder.
</div>
);
}
I have a feeling this might have something to do with the other react application in the django project but if anyone can see something that I can't I would appreciate the help.
Edit
I realised that I was trying to call useState() in the useEffect hook when I should have been using the setPosts function that was already defined in the function.
// The code crashes here saying that it is an invalid hook call
It crashes there because you are not using the state hook correctly. You should call setPosts instead of useState. See docs.

React setState late updation with API calls [duplicate]

This question already has answers here:
if-else statement inside jsx: ReactJS
(17 answers)
Closed 2 years ago.
I am new to React and as I was creating a project, I came across a rather peculiar event with my code.
It is with the async nature of the setState which doesn't let me render data on my page like i want it to.
I am trying to display files which i have in my database already, onto my webpage. But since the state is set after a while therefore i am not able to render my files onto the screen using the map function, since when it is called it is undefined.
I implplemented a get method in so as to get the json response of the files that i want to display. But as I mount my component and setstate of the Files, it shows that it doesn't have any value in it.I know it is async but i have no idea how to handle it so that i can use map to display onto the webpage.
My code is as follows:
import React, {Component} from "react";
// import axios from 'axios';
import {Link} from 'react-router-dom';
import {Styles} from '../Styling/Styles';
class FileView extends Component {
state = {
fileViewData : {}
}
// viewFunction(){
componentDidMount(){
fetch('http://127.0.0.1:8000/api/uploads/', {
method: 'GET'
})
.then((response) => {
let data = response.json();
return data;
})
.then((data) => {
this.setState({fileViewData: data}, ()=>{
console.log("hi");
});
}).catch(error => {console.log(error)})
// console.log(fileViewData);
}
render(){
return (
<React.Fragment>
<Styles>
<div className = "appbar">
<Link to='/dashboard'>
<button className="homeBtn" label="Back" >
Back
</button>
</Link>
</div>
{/* <button label="View" onClick={this.viewFunction} >
View
</button> */}
</Styles>
//.....section not working since map is not a function of undef......//
{
this.state.fileViewData.map(item =>{
return (
<h2>{item.fields.file}</h2>
);
})
}
//.......section not working ends.........//
{console.log(this.state.fileViewData)}
</React.Fragment>
);
}
}
export default FileView;
The console output is something like this:
The empty object is returned twice and then the data is returned twice.I am not running any kind of loop either.
How should I set the value so that i am able to display my files onto the screen? TIA!
Looks like your data is an array, so your initial state should also be an array
state = {
fileViewData: [],
}
Then your check for array length will be correct, but regular javascript doens't quite work the same in JSX. You'll need to conditionally render JSX.
Conditional Rendering
{this.state.fileViewData.length
? this.state.fileViewData.map(...)
: null
}
Since it seems you don't really render anything if there is no data, you can simply map the data as array::map correctly handles empty arrays.
{
this.state.fileViewData.map(...)
}
Set state to an empty array
Remove the if condition and anonymous function declaration from your map statement
You declare that function but never invoke it also you don't need it.
if you insist on checking the length
{
this.state.fileViewData.length &&
this.state.fileViewData.map(item =>{
return (
<h2>{item.fields.file}</h2>
);
})
}
You are getting multiple console logs because when you set state in React you cause the component to render again.
As for your implementation, you probably want the initial value of fileViewData to have the same data structure as what your back end hands back. Currently you start with a plain javascript object, and then turn it into an array of objects once the response comes from your back end.
Without any consideration for a loading period, a simple thing to do to make your application not crash until data loads in would be to make fileViewData an empty array when it is initialized, not an object with no keys. Then it would have the correct prototype to have the method map.

Categories

Resources