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;
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.
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>
</>
);
}