Reputation: 29
I am facing the error TypeError: Cannot read properties of undefined (reading 'map')
This code should return the name of the city in a card title which is defined in my other file but throwing an error.
Codes:
import React, {Component} from 'react';
import Body from './Body';
class Weather extends Component {
constructor(props) {
super(props);
this.state = {
weather: [],
};
}
async componentDidMount() {
const url = `http://api.weatherapi.com/v1/current.json?key=${this.props.api}&q=Jaipur&aqi=no`;
let data = await fetch(url);
let parsedData = await data.json();
this.setState({
weather: parsedData.weather,
});
}
render() {
return (
<div className="container">
<div className="row">
{this.state.weather.map((element) => {
return (
<div className="col-md-4">
<Body city={element.location.name} />
</div>
);
})}
</div>
</div>
);
}
}
export default Weather;
Upvotes: 2
Views: 18181
Reputation: 11
the simplest way is just to add "this.state.weather &&" before map method, first it will make sure that "this.state.weather" is defined and then run the code, example is below
{this.state.weather && this.state.weather.map((element) => {
return (
<div className="col-md-4">
<Body city={element.location.name} />
</div>
);
})}
Upvotes: 1
Reputation: 8925
your weather array is empty before the API call so using this.state.weather.map
will cause the error.
There are two important things before using the map
with arrayes:
check its declaration/definition by a simple if
statement:
{
if(myArrayOfData) {
myArrayOfData.map(
// rest of the codes ...
)
}
}
Or with using ?
shorthanded of if
{
myArrayOfData?.map(
// rest of the codes ...
)
}
check for the contents of the array and use the map
function after checking its length (which tells you the data has arrived from the API call etc. and is ready to process)
{
if(myArrayOfData) {
if(myArrayOfData.length > 0) {
myArrayOfData.map(
// rest of the codes ...
)
}
}
}
while the above snippet works properly, you can simplify it by checking both if
conditions together:
{
if(myArrayOfData?.length > 0) {
myArrayOfData.map(
// rest of the codes ...
)
}
}
So, simply make some changes in the return of Weather
component:
<div className="row">
{
if(this.state.weather?.length > 0) {
this.state.weather.map((element) => {
return (
<div className="col-md-4" key={element.id}> // also don't forget about the passing a unique value as key property
<Body city={element.location.name}/>
</div>
);
})
}
}
</div>
In real-world examples, you may need to show some loading components while the data is fetching.
{
if(myArrayOfData?.length > 0) {
myArrayOfData.map(
// rest of the codes ...
)
} else {
<Loading />
}
}
const anEmptyArray = []
if(anEmptyArray){
// rest of the codes ...
}
The result of comparison on if(anEmptyArray)
is always true
with an empty array.
Upvotes: 6
Reputation: 102
Lets make an assumption that parsedData.weather
have correct data type. You should make a conditional logic, to check this.state.weather
should have a value from API.
Here's an example
render() {
const { weather } = this.state; // Access the state `weather`
return (
<>
<div className="container">
<div className="row">
{ /* This way use conditional ternary operator */ }
{weather.length ? weather.map((element) => {
return (
<div className="col-md-4">
<Body city={element.location.name}/>
</div>
);
}) : <span>Loading...</span>}
</div>
</div>
</>
);
}
Upvotes: 0