React Error: Cannot read property 'map' of undefined - reactjs

I have been trying to resolve this error for almost 2 hours but no luck. I have even researched and used the bind method but still no luck with mapping a props that was passed through a parent component. Your help will be greatly appreciated.
import React from "react";
import { Link } from "react-router-dom";
const PostList = ({ postItem }) => {
postItem.map((post) => (
<div className="mx-auto mb-3 card w-75" key={post.id}>
<div className="card-body">
<h5 className="card-title">{post.title}</h5>
<p className="card-text">{post.comment}</p>
<Link to="/create">
<ion-icon
style={{ color: "#fc5185", fontSize: "20px" }}
name="trash-outline"
></ion-icon>
</Link>
</div>
</div>
));
};
export default PostList;
And the parent component is
class Dashboard extends Component {
state = {
posts: [
{
id: 1,
title: "Hello",
comment: "it is sunny today",
},
],
};
createPost = (title, comment) => {
const newPost = {
id: Math.floor(Math.random() * 1000),
title,
comment,
};
this.setState({
posts: [...this.state.posts, newPost],
});
};
render() {
return (
<div>
<CreatePost createPost={this.createPost} />
<PostList postItem={this.state.posts} />
</div>
);
}
}
export default Dashboard;

I guess you have missed to add return to the PostList component, you can do it in three ways (read about arrow functions)
const PostList = ({ postItem }) => postItem.map((post) => (
<div className="mx-auto mb-3 card w-75" key={post.id}>
<div className="card-body">
<h5 className="card-title">{post.title}</h5>
<p className="card-text">{post.comment}</p>
</div>
</div>
));
const PostList = ({ postItem }) => (
postItem.map((post) => (
<div className="mx-auto mb-3 card w-75" key={post.id}>
<div className="card-body">
<h5 className="card-title">{post.title}</h5>
<p className="card-text">{post.comment}</p>
</div>
</div>
));
);
const PostList = ({ postItem }) => {
return postItem.map((post) => (
<div className="mx-auto mb-3 card w-75" key={post.id}>
<div className="card-body">
<h5 className="card-title">{post.title}</h5>
<p className="card-text">{post.comment}</p>
</div>
</div>
));
}
Here is the working example

Related

React Context is not working as expected: Unable to ubdate the cartItems array and addProductToCart function isn't working

So when I click add to cart on the shop page it should add the item to the cartItem' array nothing happens, also the cartItems array appears to be of type "never[]" and I don't know how to fix that in jsx.
Project link on GitHub
cart.context.jsx file
import { useState, createContext } from "react";
export const addCartItem = (cartItems, productToAdd) => {
const existingCartItem = cartItems.find(
(cartItem) => cartItem.id === productToAdd.id
);
if (existingCartItem) {
return cartItems.map((cartItem) =>
cartItem.id === productToAdd.id
? { ...cartItem, quantity: cartItem.quantity + 1 }
: cartItem
);
}
return [...cartItems, { ...productToAdd, quantity: 1 }];
};
export const CartContext = createContext({
cartItems: [],
addItemToCart: () => {},
});
export const CartProvider = ({ children }) => {
const [cartItems, setCartItems] = useState([]);
const addItemToCart = (product) => {
setCartItems(addCartItem(cartItems, product));
};
const value = { addItemToCart, cartItems };
return <CartContext.Provider value={value}>{children}</CartContext.Provider>;
};
cart.jsx file
import CartItem from "../components/cart-item";
import { toggleClass } from "./navigation";
import { useContext } from "react";
import { CartContext } from "../context/cart.context";
import "../scss/cart.scss";
const Cart = () => {
const { cartItems } = useContext(CartContext);
return (
<div className={`card cart`} style={{ width: "18rem" }}>
<button
type="button"
className="btn-close m-2"
aria-label="Close"
onClick={toggleClass}
></button>
<div className="card-body text-center" style={{ height: "20rem" }}>
<h5 className="card-title">Cart</h5>
<div>
{cartItems.length ? (
cartItems.map((item) => <CartItem key={item.id} cartItem={item} />)
) : (
<h5>The cart is empty</h5>
)}
</div>
<button href="#" className="checkOutBtn mb-2 btn bg-dark text-white">
Check Out
</button>
</div>
</div>
);
};
export default Cart;
cart-item.jsx file
const CartItem = ({ item }) => {
const {name, imageUrl, price, quantity } = item;
return (
<div className="card mb-3">
<div className="row g-0">
<div>
<div className="col-md-4">
<img
src={imageUrl}
className="img-fluid rounded-start"
alt={name}
/>
</div>
<div className="col-md-8">
<div className="card-body">
<h5 className="card-title">{name}</h5>
<p className="card-text">{price}</p>
<p className="card-text">{quantity}</p>
</div>
</div>
</div>
</div>
</div>
);
};
export default CartItem;
productCart.jsx file
import { useContext } from "react";
import { CartContext } from "../context/cart.context";
const ProductCard = ({ product }) => {
const { addItemToCart } = useContext(CartContext);
const addProductToCart = () => addItemToCart(product);
const { name, imageUrl, price } = product;
return (
<div className="col-md-3 my-2">
<div className="card pb-3">
<div
className="image-container"
style={{ height: "300px", overflow: "hidden" }}
>
<img src={imageUrl} className="card-img-top" alt={`${name}`} />
</div>
<div className="card-body d-flex justify-content-between">
<p className="card-text d-inline">{name}</p>
<span className="d-inline text-success">{price}$</span>
</div>
<button
className="btn btn-success w-50 mx-auto"
onClick={addProductToCart}
>
Add to cart
<i className="fa fa-cart-plus mx-2" aria-hidden="true"></i>
</button>
</div>
</div>
);
};
I tried to call the addCartItem function from ProductCard and it worked and added the item to the array but the array didn't update so the cart component didn't render anything cause the array was empty.
NVM guys I found the problem I didn't set up the provider properly in the index.js
What should I've written
<BrowserRouter>
<UserProvider>
<ProductsProvider>
<CartProvider>
<App />
</CartProvider>
</ProductsProvider>
</UserProvider>
</BrowserRouter>
What I wrote
<BrowserRouter>
<UserProvider>
<App />
</UserProvider>
</BrowserRouter>
</React.StrictMode>

Can't get the data from map

Okay I should be dumb, the first console log gives me data but when I put it through map it shows undefined. I really made some digging but stuck at this stage.
import React from 'react'
const Users = ({ users } : { users:any }) => {
return (
<div>
{ console.log(users) }
{users.map(({user} : {user:any},{i} : {i:any}) => (
<div className="card">
{console.log("test: " + user)}
<div className="card-body">
<h5 className="card-title">{user.name}</h5>
<h6 className="card-subtitle mb-2 text-muted">{user.name}</h6>
</div>
</div>
))}
</div>
)
};
export default Users
TypeScript can infer the types because you have explicitly set them:
import React from 'react'
const Users = ({ users } : { users:any }) => {
return (
<div>
{ console.log(users) }
{users.map((user, i) => (
<div className="card">
{console.log("test: " + user)}
<div className="card-body">
<h5 className="card-title">{user.name}</h5>
<h6 className="card-subtitle mb-2 text-muted">{user.name}</h6>
</div>
</div>
))}
</div>
)
};
export default Users
Assuming users is an array of user objects, try this:
import React from 'react'
const Users = ({ users } : { users:any }) => {
return (
<div>
{ console.log(users) }
{users.map((user : any, i : any) => (
<div className="card">
{console.log("test: " + user)}
<div className="card-body">
<h5 className="card-title">{user.name}</h5>
<h6 className="card-subtitle mb-2 text-muted">{user.name}</h6>
</div>
</div>
))}
</div>
)
};
export default Users

Passing methods between Functional components in react

I have 2 components as below. When I try to pass a method (increment) from the component 'CartList' to 'CartItem' it says increment is undefined. The error occurs when I click on the button (Pointed in the below code). How can I solve this error?
Parent
import React, {Component} from 'react';
import CartItem from './CartItem';
import {connect} from "react-redux";
import Axios from "axios";
const mapStateToProps = ({ session}) => ({
session
});
const CartList = ({session, ...props}) => {
const cart = props.cart;
const increment = (productId) => {
const item = {
userId : session.userId,
productId: productId
};
Axios.post('http://localhost:5000/api/cart/increment', item)
.then(res=>{
if(res.status === 200){
console.log('Incremented');
}
})
};
return (
<div className="container-fluid">
{cart.map(item => {
return <CartItem key = {item.id} item={item} increment={increment}/>
})}
</div>
);
};
export default connect(
mapStateToProps
)(CartList);
Child
import React from 'react';
import {connect} from "react-redux";
const mapStateToProps = ({ session}) => ({
session
});
const CartItem = ({session ,...props}) => {
const {id,name, price, quantity} = props.item;
const {increment} = props.increment;
return (
<div className="row my-2 text-capitalize text-center">
<div className="col-10 mx-auto col-lg-2">
<img style={{width: '5rem', height: '5rem'}} className="img-fluid" alt="product "/>
</div>
<div className="col-10 mx-auto col-lg-2">
<span className="d-lg-none">Product: </span>{name}
</div>
<div className="col-10 mx-auto col-lg-2">
<span className="d-lg-none">Price: </span>{price}
</div>
<div className="col-10 mx-auto col-lg-2 my-2 my-lg-0">
<div className="d-flex justify-content-center">
<div>
<span className="btn btn-black mx-1" >-</span>
<span className="btn btn-black mx-1">{quantity}</span>
<span className="btn btn-black mx-1" onClick={() => increment(id)}>+</span> //<- Error occurs if I click on this button
</div>
</div>
</div>
<div className="col-10 mx-auto col-lg-2">
<div className="cart-icon" >
<i className="fas fa-trash"/>
</div>
</div>
<div className="col-10 mx-auto col-lg-2">
<srong>Total: ${50}</srong>
</div>
</div>
);
};
export default connect(
mapStateToProps
)(CartItem);
I tried implementing the function directly in the child component it is working fine. This method is responsible for incrementing the quantity of a product in the database(MongoDB). But updated value does not display. Because of that I implemented the 'increment' function within the parent component
The issue is this line:
const {increment} = props.increment;
That is trying to access props.increment.increment, which is undefined. You either meant to do
const increment = props.increment;
or
const {increment} = props;
Hi Please replace this line
Because when you destructuring props you don't need map key from props
for better understanding console your props or have a look at this document.
https://medium.com/#lcriswell/destructuring-props-in-react-b1c295005ce0
const {increment} = props.increment;
instead of
const {increment} = props;
I hope it works.
Thanks

react redux two components

I have a component that will display the products which is coming from backend and a component that receives the products to filter but I have doubt that receive by redux my product list.
should i put for my filters component receive?
or should return the same as I get in my product component?
or should I create an action to filter what I need already?
my home:
return (
<div className="container">
<h1>Shopping</h1>
<hr />
<div className="row">
<div className="col-md-3"><Filters/></div>
<div className="col-md-9"><Products/></div>
</div>
</div>
)
my component products:
import React, { Component } from 'react'
import {connect} from 'react-redux'
import { ProductsFetchData } from '../../store/actions/productsFetch';
import util from '../../util';
class HomeProducts extends Component {
componentDidMount() {
this.props.fetchData('/products');
}
render() {
const productItems = this.props.products.map( product => (
<div className="col-md-4 pt-4 pl-2">
<div className = "thumbnail text-center">
<a href={`#${product.id}`} onClick={(e)=>this.props.handleAddToCard(e,product)}>
<p>
{product.name}
</p>
</a>
</div>
<b>{util.formatCurrency(product.price)}</b>
<button className="btn btn-primary" onClick={(e)=>this.props.handleAddToCard(e,product)}>Add to Cart</button>
</div>
)
)
return (
<div className="container">
<div className="row">
{productItems}
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
products: state.Products,
hasErrored: state.ProductsHasErrored,
isLoading: state.ProductsIsLoading
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchData: () => dispatch(ProductsFetchData())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(HomeProducts);
my components filter
import React, { Component } from 'react';
import './style.css'
class FilterHome extends Component {
render() {
return (
<>
<div className="row">
<button className="filterbt btn btn-danger btn-rounded">Filters</button>
<div className=" mt-4 d-flex flex-column">
<p className="textCategory">CATEGORY</p>
<div className="category d-flex flex-column">
<p>Stat Trak</p>
<p>Normal</p>
</div>
<p className="textCategory">EXTERIOR</p>
<div className="category d-flex flex-column">
<p>Factory New ()</p>
<p>Minimal Wear ()</p>
<p>Field Tested ()</p>
<p>Well Worn ()</p>
<p>Battle Scarred ()</p>
</div>
</div>
</div>
</>
)
}
}
export default FilterHome;
1.redux-state: this is the registering point for all your api responses(all the data from back-end is stored here as prestine and is available as props to any container when you mapStateToProps).
2.local-state: this lives only in your container and all it's child components.
3.filter:
a)from server:
you make a request to the server and get a response of filtered products. this
is more practical.
eg: you have /products?page=1 and you want to search it by some category, let's
say by a specific company. with the data you have at the moment(page 1), you
may have only let's say 1 or even no product relevant to that company, but in fact there are n-numbers of products of the same company available at the server. so this can only be assumed as the
most practical way.
b)filtering from the local-state:
if this is what your'e trying to achieve,
1. you need only one container, HomeProducts
2. make ProductItems as a component. wer'e gonna reuse this component to render both.
**you wrote your filter as an independent container. but those filter functionality should be availabe inside the home page itself. isn't it, i mean you're filtering from the home page itself not from another page. if so, add it to the home page itself
1.HomePage
import React, { Component } from 'react'
import {connect} from 'react-redux'
import { ProductsFetchData } from '../../store/actions/productsFetch';
import util from '../../util';
import ProductItems from '<<..your relative path>>'
import FilterHome from '<<..your relative path>>'
class HomeProducts extends Component {
constructor(props) {
super(props)
this.state = {
productList: null,
}
}
componentDidMount() {
//this.props.fetchData('/products');
this.props.fetchData('page=1');
}
componentWillReceiveProps(nextProps) {
const { productList } = this.state
const { products } = this.props
// this only handles when local state is empty. add your logic here..
!productList && this.setState(prevState => ({
...prevState,
productList: products,
}))
}
handleFilter = (category) => {
// if it's an api call
const { fetchData } = this.props
fetchData(`page=1&category=${category}`)
//or you simply want to filter this local state(not-recommended)
const { products } = this.props
const productsList = [...products].filter(item => item.category === category)
this.setState(prevState => ({
...prevState,
productsList,
}))
}
render() {
const { productList } = this.state
const { handleFilter } = this
return (
<div className="container">
<FilterHome {...{ handleFilter }} />
<ProductItems {...{ productsList }} />
</div>
)
}
}
const mapStateToProps = (state) => {
return {
products: state.Products,
hasErrored: state.ProductsHasErrored,
isLoading: state.ProductsIsLoading
};
};
//it may not suit depends on your api, but you get the idea..
const mapDispatchToProps = (dispatch) => {
return {
// fetchData: () => dispatch(ProductsFetchData())
fetchData: params => dispatch(ProductsFetchData(`/products?${params}`))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(HomeProducts);
2.ProductItems
import React from 'react'
const ProductItem = ({ product }) => {
return (
<div className="col-md-4 pt-4 pl-2">
<div className = "thumbnail text-center">
<a href={`#${product.id}`} onClick={(e)=>this.props.handleAddToCard(e,product)}>
<p>
{product.name}
</p>
</a>
</div>
<b>{util.formatCurrency(product.price)}</b>
<button className="btn btn-primary" onClick={(e)=>this.props.handleAddToCard(e,product)}>Add to Cart</button>
</div>
)
}
const ProductItems = ({ productList }) => {
return (
<div className="row">
{productList && productList.map(product => <ProductItem key={product.id} {...{ product }} />)}
</div>
)
}
export default ProductItems
3.FilterHome
import React from 'react'
const FilterHome = ({ handleFilter }) => {
return (
<div className="row">
<button className="filterbt btn btn-danger btn-rounded">Filters</button>
<div className=" mt-4 d-flex flex-column">
<p className="textCategory">CATEGORY</p>
<div className="category d-flex flex-column">
<a href="" className="text-decoration-none" onClick={() => handleFilter('stat_trak')}><p>Stat Trak</p></a>
<a href="" className="text-decoration-none" onClick={() => handleFilter('normal')}><p>Normal</p></a>
</div>
<p className="textCategory">EXTERIOR</p>
<div className="category d-flex flex-column">
<a href="" className="text-decoration-none" onClick={() => handleFilter('factory_new')}><p>Factory New ()</p></a>
<a href="" className="text-decoration-none" onClick={() => handleFilter('minimal_wear')}><p>Minimal Wear ()</p></a>
<a href="" className="text-decoration-none" onClick={() => handleFilter('field_tested')}><p>Field Tested ()</p></a>
<a href="" className="text-decoration-none" onClick={() => handleFilter('well_worn')}><p>Well Worn ()</p></a>
<a href="" className="text-decoration-none" onClick={() => handleFilter('battle_scarred')}><p>Battle Scarred ()</p></a>
</div>
</div>
</div>
)
}
export default FilterHome
i roughly re-wrote it, may contain bugs..
first add it to the local state and employ a call back to the filter component..
handleFilter = (category) => {
const { Products } = this.state
const products = {...Products}
//or depends on the type, so it wont mutate the real data>> const products = [...Products]
return products.filter(item => item.category === category)
}
this is what is understood from your comment. is that it?

How to load firestore subcollection to redux store?

I'm having trouble loading firestore subcollection in redux store. I'm trying to load respective subcollection inside the child component.
The component structure looks like this:
Dashboard
--Project list
--project summary
--comments
I have loaded collection named Projects using firestoreConnect inside Dashboard component passing data to Project list mapping data to Project Summary. comments is a child component of project summary
My firestore collection structure looks like this:
Projects
--project1
--comments
Dashboard:
class Dashboard extends Component {
render() {
const { projects, auth, notifications } = this.props
if (!auth.uid) return <Redirect to='/signin' />
return(
<div className="dashboard container">
<div className="row">
<div className="col s12 m8 offset-m2 l8">
<ProjectList projects={projects} />
</div>
<div className="col s12 offset-m1 hide-on-med-and-down l4">
<Notification notifications={notifications} />
</div>
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
// console.log("ref: ",state)
return {
projects: state.firestore.ordered.projects,
initials: state.firebase.profile.initials,
auth: state.firebase.auth,
notifications: state.firestore.ordered.notifications
}
}
export default compose (
connect(mapStateToProps),
firestoreConnect([
{ collection: 'projects', orderBy: ['createdAt', 'desc'] },
{ collection: 'notifications', limit: 3, orderBy: ['time', 'desc'] }
])
)(Dashboard)
Project list Component:
const ProjectList = ({ projects }) => {
return(
<div className="project-list section">
{projects && projects.map((project) => {
return(
<div key={project.id}>
<ProjectSummary project={project}/>
</div>
)
})}
</div>
)
}
export default ProjectList
Project summry:
const ProjectSummary = ({ project }) => {
return(
<div className="card z-depth-0 project-summary show-up post">
<div className="name">
<div className="">
<span className="btn btn-floating z-depth-0 black user-indicator">
{project.authorFirstName && project.authorFirstName[0]}{project.authorSecondName && project.authorSecondName[0]}
</span>
<span> {project.authorFirstName {project.authorSecondName} </span>
<span className="right options"> <i className="material-icons">more_vert</i> </span>
</div>
</div>
<div className="card-image">
<img src={project.imageUrl} alt="art" />
<PostCredits />
</div>
<div className="card-reveal">
<Comments id={project.id} />
</div>
<div className="card-content">
<Link to={'/projectdetails/' + project.id} className="black-text">
<p className="post-title"> {project.title} </p>
</Link>
<p className="grey-text lighten-3 load-comments activator"> Load comments </p>
<p className="grey-text date-format">
{project.createdAt && project.createdAt
.toDate()
.toLocaleDateString('indian', {
year: "numeric", month: "short", day: "numeric"
})}
</p>
</div>
<div className="add-comment">
<AddComment projectId={project.id} />
</div>
</div>
)
}
export default ProjectSummary
Comment:
const Comment = (props) => {
return(
<div>
<div className="loaded-comments">
<span className="card-title grey-text text-darken-4"> Comments <i className="material-icons right">close</i> </span>
<p> <span className="commentor"> Joe </span> </p>
</div>
</div>
)
}
const mapStateToProps = (state, ownProps) => {
console.log("state: ", state)
return {
}
}
export default compose (
connect(mapStateToProps),
firestoreConnect(props => {
return [
{ collection: 'projects',
doc: props.id,
subcollections: [
{
collection: 'comments'
}
]
}
]
})
)(Comment)
Expected Result:
*Want to load Project collection's subcollection named Comments
Actual Result:
*No error occurs but still no data is loaded.
Thanks in advance!
There is a possibility that you just don't see that sub-collection.
If you look under in your browser console, navigate to firebase > ordered > prjoects > comments. you should be able to see an array of your comment
Example:
|-firestore
|--ordered:
|---projects: Array(1)
|----0:
|-----id: *uid*
|------comments: Array(2)
|-------0: {*comment details*}
|-------1: {*comment details*}
|------length: 2
|-----__proto__: Array(0)
After you can map your data based on your key.
const mapStateToProps = (state) => {
return {
projects: state.firestore.ordered.projects.comments,
}
}
Once connected you will be able to use your data.

Resources