I'm getting Cannot read property 'map' of undefinedin my React app - while that typically means that I'm searching for the incorrect data type (object vs. array, for instance), I'm unable to console.log my API URL, which makes me think something else is wrong.
Here's my code, everything exists in my App.js file so it's not an issue with calling other components or anything:
import React, { Component } from 'react';
import './App.css';
const APIURL = `https://api.openbrewerydb.org/breweries?`;
const citySearch = `by_state=new_york`;
class App extends Component {
constructor() {
super()
this.state = {
error: null,
isLoaded: false,
breweries: []
};
}
componentDidMount() {
fetch(APIURL + citySearch)
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
breweries: result.breweries
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { error, isLoaded, breweries } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<ul>
{breweries.map(brewery => (
<li key={brewery.id}>
{brewery.name} {brewery.city}
</li>
))}
</ul>
);
}
}
}
export default App;
Here's the data I'm trying to get - if I copy and paste my APIURL into my browser, I get the data - so I'm not sure where undefined is coming from:
[{"id":4581,"name":"Adirondack Toboggan Company Microbrewery","brewery_type":"micro","street":"202A W Main St","city":"Gouverneur","state":"New York","postal_code":"13642-1334","country":"United States","longitude":"-75.4748923846074","latitude":"44.3323731052224","phone":"3157716313","website_url":"http://www.adktoboggan.net","updated_at":"2018-08-24T15:37:58.899Z","tag_list":[]}, (AND SO ON)...
As I've just started this app, I'm just trying to make sure my fetch request works so I can build out components.
Am I getting undefined because of how I'm searching for the data (which seems to be the solution to other questions like mine) or is something else going on that prevents me from even logging my APIURL const?
Related
Hello to everyone who reads this and can help me out. So I am working on a personal project on a quest to learn ReactJS and my website used to just be a WordPress site. Now I want to use React as my frontend and WordPress as my backend, however, I get this annoying error and I swear I've spent days googling various error messages as they popped up and solved a few but this latest one just won't go.
I'm not sure if it's something to do with my local setup or just that the variable being fetched (an array of posts) is undefined. I am using the AJAX and Fetch API tutorial from the React documentation cause I like how they did it, but it doesn't seem to work with passing the values into the array for some reason.
I will post my code blow:
Home.js Component:
import React, { useEffect, useState } from 'react';
import Navigation from './Navigation';
class Home extends React.Component {
constructor (props) {
super (props);
this.state = {
error: null,
postsLoaded: false,
posts: []
};
}
componentDidMount() {
const wordPressSiteURL = 'http://localhost/wordpress';
const postsEndpoint = '/wp-json/wp/v2/posts';
const endpoint = wordPressSiteURL + postsEndpoint;
fetch(endpoint)
.then(response => response.json())
.then(response => console.log('Our response being converted to JSON ', response))
.then(result => console.log('The results of our response ', result))
.then(
(result) => {
this.setState({
postsLoaded: true,
posts: result.posts
});
},
(error) => {
this.setState({
postsLoaded: true,
error
});
}
)
}
render() {
const {error, postsLoaded, posts} = this.state;
// console.log('This state ', this.state);
// console.log('Check for errors ', error);
// console.log('Initial posts array values ', posts);
if (error) {
return <><Navigation /><div>Error: {error.message}</div></>;
} else if (!postsLoaded) {
return <><Navigation /><div>Loading...</div></>;
} else {
return (
<div>
<Navigation/>
<ul>
{posts && posts.map((post) => (
<li key={post.id}>
{post.content}
</li>
))}
</ul>
</div>
);
}
}
}
export default Home;
App.js Page:
import React from 'react';
import Home from './components/Home';
import './App.css';
class App extends React.Component {
render() {
return (
<div className='App'>
<Home/>
</div>
);
}
}
export default App;
Error Message: Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'posts')
at fetch.then.then.then.then.setState.postsLoaded (Home.js:68:1)
My other error message that "appears" to be fixed: Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'map')
I am so annoyed I tried more than a dozen ways of fetching the posts array from my local WordPress REST API and in the return section changing how I was mapping over my posts.
When I made this change the specific map error went away:
<ul>
{posts && posts.map((post) => (
<li key={post.id}>
{post.content}
</li>
))}
</ul>
Instead of just posts.map, I put the "posts && posts.map" in to enforce some type of check before the array is mapped over. I would really appreciate help from someone who understands this error message and working with React specifically.
Don't use multiple then, as I saw in your code you used multiple then in the fetch call. look at the below code for your fetch call.
fetch(endpoint)
.then(response => response.json())
.then(
(result) => {
console.log('Our response being converted to JSON ', result)
console.log('The results of our response ', result)
this.setState({
postsLoaded: true,
posts: result.posts
});
},
(error) => {
this.setState({
postsLoaded: true,
error
});
}
)
I am new here and with React. I tried to get data via APı but I could not get any. Also, I did not get any error message as well. This is my code. I tried to add if statement to check whether data is fetched or not. When I check console I got the error message. Where did I wrong? Thanks for your time.
import React, {Component} from "react";
class PokemonList extends Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: [],
};
}
componentDidMount() {
fetch('https://pokeapi.co/api/v2/pokemon?limit=20')
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result.items
});
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(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 {
return (
<ul>
{items ? items.map(item => (
<li>
<p key={item.id}>{item.name}</p>;
</li>
)) :
console.log('no data')}
</ul>
);
}
}
}
export default PokemonList;
The Pokémon items you're trying to get have a results key, not items. So replace result.items with result.results and see what happens.
What I want is, when I get no data from the api, instead of this No data, I want A notification or toast.error to get displayed.
shops.jsx
import React from 'react';
import './shops.css';
import Shop from './shop'
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
require('dotenv').config()
const TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1ZjFiMjNlYTQxNmJhMjQ3YjQ5MDk4Y2IiLCJlbWFpbCI6Img1aW1icjQ0NGQ7QHR5cC5pbiIsImlhdCI6MTU5NjgxMTU5MSwiZXhwIjoxNTk2ODE1MTkxfQ.UyrUkbNWzenf50FL8AZE1iZaii11P7MwdXpKmoCB9nM";
class Shops extends React.Component {
constructor(props) {
super(props);
this.state = {
shops: []
};
}
componentDidMount() {
console.log(process.env.REACT_APP_BaseURL);
// replace with correct URL: http://localhost:5000/api/shops/allShops
fetch(process.env.REACT_APP_BaseURL, {
method: "get",
headers: new Headers({
Authorization: `Bearer ${TOKEN}`
})
})
.then(response =>response.json())
.then(data => {
this.setState({ shops: data.fetchedShops });
toast.success("API LOADED SUCCESSFULLY","SUCCESS");
})
.catch(err =>{
console.log("Error", err);
if(err){
toast.error("error occured");
}
});
}
render() {
const shops =
this.state.shops.length > 0 ?
this.state.shops.map(item => (
<Shop name={item.shopname} address={item.address} mobile={item.phoneNumber} />
))
: <span >No data</span>;
console.log(this.state.shops);
return <div id="container">{shops}</div>;
}
}
export default Shops;
In the 6th line you can see <span >No data</span> instead of this I want a toast.error notification, but when I write toast.error("No data"); instead of this span i got something like this instead of error notification
If you want to toast that there is no data when the array is empty it needs to be done in two steps since render is a pure function, i.e. without side effects
Issue toast side-effect in component lifecycle functions, i.e. componentDidMount and/or componentDidUpdate
Render null when toasting no data, or since the map can handle empty arrays without issue, just return the empty map result array
Code
class Shops extends Component {
state = {
shops: []
};
checkShops = () => {
const { shops } = this.state;
if (!shops.length) {
toast.error("No Data");
}
};
componentDidMount() {
this.checkShops(); // not really needed if fetch for data first
}
componentDidUpdate() {
this.checkShops();
}
render() {
const { shops } = this.state;
return (
<div id="container">
{shops.map((item) => <div>Data</div>)}
</div>
);
}
}
So I am trying to build a cart using react and express. The backend is working fine. I am using postman to test my endpoint and it is giving me the correct response. However, it is the react frontend that is causing problems.
I am trying to use map function on the items array which has been set to the response from the server, but it gives me an error:
TypeError: Cannot read property 'map' of undefined
Here is my code:
Cart.js
import React, { Component } from "react";
import axios from "axios";
import CartItem from "./cart1-item.component.js";
import "bootstrap/dist/css/bootstrap.min.css";
import { throws } from "assert";
export default class Cart extends Component {
constructor(props) {
super(props);
this.state = {
items: []
};
}
componentDidMount() {
axios
.get("http://localhost:4000/cart/")
.then(response => {
this.setState({ items: response.data });
})
.catch(function(err) {
console.log(err);
});
}
checkItems() {
return this.state.items.items.map((currItem, i) => {
return <CartItem book={currItem} key={i}></CartItem>;
});
}
Calculate = item => {
return item.qty * item.price;
};
render() {
return (
<div className="container">
<div className="row">{this.checkItems()}</div>
</div>
);
}
}
CartItem.js
import React, { Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
const CartItem = props =>
props.items.map(item => {
return <div>{item.title}</div>;
});
export default CartItem;
And the postman response for '/cart'
{
"items": [
{
"item": "5dd7668f33c21d811b74f403",
"title": "Modern PHP",
"price": 25.65,
"qty": 1
}
],
"total": 25.65
}
here is also the server.js code I dont understand why my array is empty when the postman is giving me a response, that indicates my endpoints work correctly.
cartRoutes.route("/").get(function(req, res) {
var cart = req.session.cart;
var displayCart = { items: [], total: 0 };
var total = 0;
for (var item in cart) {
displayCart.items.push(cart[item]);
total += cart[item].qty * cart[item].price;
}
displayCart.total = total;
return res.json(displayCart);
});
cartRoutes.route("/:id").post(function(req, res) {
req.session.cart = req.session.cart || {};
var cart = req.session.cart;
let id = req.params.id;
Book.findById(id, function(err, book) {
if (err) {
console.log(err);
}
if (cart[id]) {
cart[id].qty++;
} else {
cart[id] = {
item: book._id,
title: book.title,
price: book.price,
qty: 1
};
}
res.redirect("/cart");
});
});
I have already spent a day and a half trying to resolve this on my own. Any help would be immensely appreciated.
As another answer is pointing your initialState is incorrect as you are accessing in the checkItems method, you have to preserve the structure.
I could suggest to mantain certain stucture, in your case looks like this is your initialState :
this.state = {
items: []
};
So when you are calling you method checkItems you are accessing it the wrong way:
checkItems() {
return this.state.items.items.map((currItem, i) => {
return <CartItem book={currItem} key={i}></CartItem>;
});
}
So as far as i can see when you receive your response you are modifying your initialState structure.
To fix this i suggest you this minor change (just change the response.data to response.data.items):
componentDidMount() {
axios
.get("http://localhost:4000/cart/")
.then(response => {
this.setState({ items: response.data.items });
})
.catch(function(err) {
console.log(err);
});
}
And the method checkItems as well:
checkItems() {
return this.state.items.map((currItem, i) => {
return <CartItem book={currItem} key={i}></CartItem>;
});
}
This happens because when you are loading the app, your initial state is wrong. this.state.items is an array, but this.state.items.items is undefined, and for the .map() function to work you need an array.
So your initial state should look something like this:
this.state = {
items: {
items: []
}
};
It seems like the structures of your initial data and data from the api do not match. You need to change the checkItems method and also set the nested items to the state:
componentDidMount() {
axios
.get("http://localhost:4000/cart/")
.then(response => {
this.setState({ items: response.data.items }); // set items to the state
})
.catch(function(err) {
console.log(err);
});
}
checkItems() {
return this.state.items.map((currItem, i) => { // items is one level deep now
return <CartItem book={currItem} key={i}></CartItem>;
});
}
The reason for the error is that your initial state is items: [], which is what being used for the first render. So basically in checkItems you're trying to access items property of an empty array.
Edit: You're also accessing incorrect props in the child component, it'd be book, not items, since it's what you're passing:
const CartItem = ({book}) => {
return <div>{book.title}</div>
}
i am new in react js,and i am learning to create a React application and I got a problem with mapping function:
Here's my request and how I am attempting to render the data:
class Patients extends Component {
constructor(props) {
super(props)
this.state = {
patients: []
}
}
componentDidMount() {
api.getPatients()
.then( patients => {
console.log( patients)
this.setState({
patients: patients
})
})
.catch(err => console.log(err))
}
render() {
return (
<div className=" Patientss">
<h2>List of Patient</h2>
{this.state.patients.map((c, i) => <li key={i}>{c.name}</li>)}
</div>
);
}
}
export default Patients;
here my api calling
import axios from 'axios';
const service = axios.create({
baseURL: process.env.NODE_ENV === 'production' ? '/api' : 'http://localhost:3000/patient',
});
const errHandler = err => {
console.error(err);
throw err;
};
export default {
service: service,
getPatients() {
return service
.get('/')
.then(res => res.data)
.catch(errHandler);
},
}
and I get the following error:
TypeError: this.state.patients.map is not a function
i've try to use slice aswell but it didnt work, anyone know whats wrong with my code?`
Based on the symptoms (heh), the patients object you get in api.getPatients() isn't an array.
console.log() it to see what it actually is.
EDIT: Based on the comments, the patients object looks like
{
count: 24,
patient: [...],
}
so the this.setState() call needs to be
this.setState({patients: patients.patient})
You can also do something like this as an conditional rendering. It will check that if this.state.patient exists then only it will go ahead and call this.state.patients.map function. It will also ensure that you don't receive any errors later on due to bad responses.
I updated your Patients Code example.
class Patients extends Component {
constructor(props) {
super(props)
this.state = {
patients: []
}
}
componentDidMount() {
api.getPatients()
.then( patients => {
console.log( patients)
this.setState({
patients: patients
})
})
.catch(err => console.log(err))
}
render() {
return (
<div className=" Patientss">
<h2>List of Patient</h2>
{ this.state.patients && this.state.patients.map((c, i) => <li key={i}>{c.name}</li>)}
</div>
);
}
}
export default Patients;
I hope it helps. Thanks!!