Reputation: 409
I'm following the ReactJS AJAX and APIs tutorial. I wrote a simple API in Spring, then a React component to consume that API at http://localhost:8080
. The API currently returns a list of two items as follows:
[
{brand:"Asus", make:"AAA"},
{brand:"Acer", make:"BBB"}
]
Here's what my component looks like:
import React from 'react';
import ReactDOM from 'react-dom';
import { environment } from '../environment/environment';
export class ComputerList extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: [
{brand: null, make: null}
]
};
}
componentDidMount() {
fetch("http://localhost:8080/computers")
.then(res => res.json())
.then(
(result) => {
// correctly displays the results
console.log(result);
this.setState({
isLoaded: true,
items: result.items
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { error, isLoaded, items } = this.state;
if(error) {
return(<div>Error: {error.message}</div>);
}
else if(!isLoaded) {
return(<div>Loading...</div>);
}
else if(items) {
console.log(items);
// error here: cannot read property "map" of undefined
// because this.state.items is undefined somehow?
return(
<ul>
{items.map(item => (
<li key={item.make}>{item.brand} {item.make}</li>
))}
</ul>
);
}
}
}
At line 24, the results are successfully retrieved and logged.
But at line 54, when I try to map each result to a <li>
item, the TypeError
is thrown because items
is somehow undefined? I've followed the answer to a similar question by initializing items
at line 12, and checking items
at line 48, to no avail.
How can I fix this?
Upvotes: 4
Views: 16943
Reputation: 33994
JUst do this. Avoid if loops in render instead use && operator or ternary operator directly in return to manage if checks
return(
<ul>
{!isLoaded && <div>Loading...</div>}
{error && !isLoaded && <div>Error: {error.message}</div>)}
{!isLoaded && items && items.map(item => (
<li key={item.make}>{item.brand} {item.make}</li>
))}
</ul>
);
Also your make key in object initially contains null value hence you cannot assign that as a key. Instead what you can do is try to generate unique id for object in your array from the backend or else use something like below
return(
<ul>
{items && items.map((item, index) => (
<li key={"key"+index}>{item.brand} {item.make}</li>
))}
</ul>
);
Excuse me for typo issues because I am answering from my mobile
Upvotes: 0
Reputation: 409
Thanks to @DacreDenny for the advice :)
Line 27: items: result.items
. This line expects the response to contain an Object named "items".
But my API only returns an Array of objects. So I changed the line to
Line 27 to: items: result
. This saves the entire array to state.items
. Then it can be properly mapped and rendered.
Upvotes: 1
Reputation: 30360
This is likely due to the type of items
being something other than an array, which is why the map()
method would not be defined.
For a more robust render()
method, you can replace else if(items) {
with else if(Array.isArray(items)) {
, which should protect against the error message you're seeing:
render() {
const { error, isLoaded, items } = this.state;
if(error) {
return(<div>Error: {error.message}</div>);
}
else if(!isLoaded) {
return(<div>Loading...</div>);
}
else if(Array.isArray(items)) { // <-- update here
console.log(items);
// error here: cannot read property "map" of undefined
// because this.state.items is undefined somehow?
return(
<ul>
{items.map(item => (
<li key={item.make}>{item.brand} {item.make}</li>
))}
</ul>
);
}
}
Hope that helps!
Upvotes: 2