Cannot define properties of undefined - reactjs

I am having an issue with my skillImage components.
I am not sure where the issue is coming from.
I get error message cannot read properties of undefined : id but it has been destructured correctly.
I have put below the following components involved, SkillContext, Skill, SkillsList.
The line of code in question is const { skillCard: {id, skill} } = useContext(SkillContext);
Can someone tell me what is wrong, please?
Thanks in advance
skill.js
import Image from "next/dist/client/image";
import { FaTimes, FaRegCalendarAlt, FaSchool,FaSpinner } from "react-icons/fa";
import styles from '../../styles/Home.module.scss'
//import { FaStar } from 'react-icons/fa';
import { AiFillStar , AiOutlineStar } from 'react-icons/ai'
import { useState, useContext } from 'react';
import { CourseFilterContext } from "../contexts/CourseFilterContext";
import { SkillProvider, SkillContext } from "../contexts/SkillContext";
function Course({ courseTitle, hours, level, year }) {
return (
<>
<span className={styles.course}>
<strong>Course :</strong> {courseTitle}{" "}
</span>
<span className={styles.course}>
<strong>Hours: </strong> {hours}
</span>
<span className={styles.course}>
<strong>Level :</strong> {level}
</span>
<span className={styles.course}>
<strong>Year :</strong> {year}
</span>
</>
);
}
function Courses() {
const { courseYear } = useContext(CourseFilterContext);
const { skillCard } = useContext(SkillContext);
const courses = skillCard.courses;
const level = skillCard.level;
return (
<div className={styles.courseBox, "h-250"}>
{courses
.filter(function (course) {
return course.year === courseYear;
})
.map(function (course) {
return (
<Course {...course} level={level} key={skillCard.courses.id} />
)
})
}
</div>
);
}
function SkillImage() {
const { skillCard: {id, skill} } = useContext(SkillContext);
,
return (
<div className="speaker-img d-flex flex-row justify-content-center align-items-center h-300">
<Image
src={`/images/skill-${id}.png`}
className="contain-fit"
alt={`${skill}`}
width="300"
height="300"
/>
</div>
);
}
function SkillFavorite () {
const { skillCard, updateRecord } = useContext(SkillContext);
const [inTransition, setInTransition] = useState(false);
function doneCallBack () {
setInTransition(false);
console.log(`In SkillFavorite: doneCallBack ${new Date().getMilliseconds()}`)
}
return (
<div className={styles.icon}>
<span onClick={function () {
setInTransition(true);
updateRecord(
{
...skillCard, favorite: !skillCard.favorite,
},
doneCallBack
)
}}>
{ skillCard.favorite === false ?
<AiFillStar fill="orange"/> : <AiOutlineStar fill="yellow"/>
}{" "}
Favorite{" "}
{ inTransition === true ? (
<span><FaSpinner className="fa-spin" fill="orange"/></span>
): null}
</span>
</div>
)
}
function SkillDescription() {
const { skillCard } = useContext(SkillContext);
const { skill, description, school, } = skillCard;
return (
<div>
<h3 className="text-truncate text-justify w-200">
{skill}
</h3>
<SkillFavorite />
<p className="text-justify skill-info">{description}</p>
<div className="social d-flex flex-row mt-4 justify-content-between">
<div className="school mb-2">
{/* <FaSchool className="m-3 align-middle " fill="#000"/> */}
<h5>School</h5>
<h6>{school}</h6>
</div>
<div className="calendar mb-2">
{/* <FaRegCalendarAlt className="m-3 align-middle " fill="#000"/> */}
{/* <h5>Year</h5>
<h6>{courses[0].year}</h6> */}
</div>
</div>
</div>
);
}
function SkillAction () {
return (
<div className="row mb-2">
<button
className="btn btn-primary w-100"
onClick={() => handleEditClick()}
>Edit
</button>
<div className="row mt-2">
<FaTimes className="m-3 " fill="#ffc800" onClick={() => handleDeleteClick(idx)} />
</div>
</div>
);
}
function Skill({ skillCard, updateRecord, insertRecord, deleteRecord }) {
const { showCourses } = useContext(CourseFilterContext);
return (
<SkillProvider skillCard={skillCard} updateRecord={updateRecord} insertRecord={insertRecord} deleteRecord={deleteRecord}>
<div className="col-xs-12 col-sm-12 col-md-6 col-lg-4 col-sm-12 col-xs-12">
<div className="card card-height p-4 mt-4 mb-2 h-300">
<SkillImage />
<div className="card-body">
<SkillDescription />
{showCourses === true ?
<Courses /> : null}
</div>
<div className="card-footer">
<SkillAction />
</div>
</div>
</div>
</SkillProvider>
);
}
export default Skill;
SkillsList
import Skill from "../components/Skill";
import styles from "../../styles/Home.module.scss";
import ReactPlaceholder from "react-placeholder";
import useRequestDelay, { REQUEST_STATUS} from "../hooks/useRequestDelay";
import { data } from '../../SkillData';
import { CourseFilterContext} from "../contexts/CourseFilterContext";
import { useContext } from "react";
function SkillsList () {
const {
data: skillData,
requestStatus,
error,
updateRecord,
insertRecord,
deleteRecord
} = useRequestDelay(2000, data)
const { searchQuery, courseYear } = useContext(CourseFilterContext);
if(requestStatus === REQUEST_STATUS.FAILURE) {
return(
<div className="text-danger">
Error: <b>Loading Speaker Data Failed {error}</b>
</div>
)
}
//if (isLoading === true) return <div className="mb-3">Loading...</div>
return (
<div className={styles.container, "container"}>
<ReactPlaceholder
type="media"
rows={15}
className="skillsList-placeholder"
ready={requestStatus === REQUEST_STATUS.SUCCESS}
>
<div className="row">
{skillData
.filter(function (skillCard) {
return (
skillCard.skill.toLowerCase().includes(searchQuery)
);
})
.filter(function (skillCard) {
return skillCard.courses.find((course) => {
return course.year === courseYear;
})
})
.map(function (skillCard) {
return (
<Skill
key={skillCard.id}
skillCard={skillCard}
updateRecord={updateRecord}
insertRecord={insertRecord}
deleteRecord={deleteRecord}
/>
);
})}
</div>
</ReactPlaceholder>
</div>
);
}
export default SkillsList;
SkillContext
import { createContext } from "react";
const SkillContext = createContext();
function SkillProvider({children, skillCard, updateRecord, insertRecord, deleteRecord}) {
return (
<SkillContext.Provider
value={skillCard, updateRecord, insertRecord, deleteRecord}>
{children}
</SkillContext.Provider>
)
}
export { SkillContext, SkillProvider}

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>

how to add data in localstorage in react

I am arslan Chaudhry. currently, I am working on an eCommerce site where I will store data in local storage because I don't have too much backend knowledge. how I can add functionality like delete and update on another folder.
my code is given below.
Books.js
import React from "react";
import "./components/book.css";
import Carousel from "react-multi-carousel";
import "react-multi-carousel/lib/styles.css";
import { FaShoppingCart } from "react-icons/fa";
import { useEffect } from "react";
import { useState, useRef } from "react";
import { useNavigate } from "react-router-dom";
import { createContext } from "react";
const Books = () => {
let arr=()=>{
let dat=localStorage.getItem("products")
if (dat) {
return JSON.parse(dat)
}
else{
return []
}
}
const [booksData, setbooksData] = useState([]);
const [productData, setproductData] = useState(arr());
let nav = useNavigate();
// slider
const responsive = {
superLargeDesktop: {
breakpoint: { max: 4000, min: 3000 },
items: 5,
},
desktop: {
breakpoint: { max: 3000, min: 1024 },
items: 3,
},
tablet: {
breakpoint: { max: 1024, min: 464 },
items: 2,
},
mobile: {
breakpoint: { max: 464, min: 0 },
items: 1,
},
};
let croser = useRef("");
let loding = useRef("");
const getJason = async () => {
try {
let fea = await fetch(
"https://script.google.com/macros/s/AKfycbxFCG7S-kjncQZwvcMnqq4wXoBAX8ecH1zkY2bLP7EE-YHlnKbiJ3RUuHtWLe6sIK30Kw/exec"
);
let acData = await fea.json();
let itemsData = acData.shop.filter((element) => {
if (element.name) {
return element;
}
});
setbooksData(itemsData);
if (itemsData) {
croser.current.style.filter = "blur(0px)";
loding.current.style.display = "none";
}
} catch (error) {
croser.current.style.filter = "blur(0px)";
loding.current.style.display = "none";
}
};
// get product data from api
useEffect(() => {
getJason();
}, []);
// go to cart button
const goto = () => {
nav("/Cart");
};
// local data
let addNow=(e)=>{
let data=productData.find((element)=>{return element.id === e.id });
let cart;
if (data) {
cart=productData.map((element)=>{
return element.id === e.id ? {...element, quantity:element.quantity+1}:element
})
}
else{
cart=[...productData,{...e, quantity:1}]
}
setproductData(cart);
};
useEffect(() => {
localStorage.setItem("products",JSON.stringify(productData))
}, [productData])
console.log(productData);
return (
<>
<div className="row " style={{ marginTop: "10px" }}>
<div className="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div className="section-headline text-center">
<h2>Books Shop</h2>
</div>
</div>
</div>
<div className="lodingBooks" ref={loding}>
<div class="spinner-border" role="status"></div>
<h4>Please wait....</h4>
</div>
<div ref={croser}>
<div className=" shadow go_to_cart" onClick={goto}>
<i class="bi bi-cart-check text-white"></i>
</div>
<Carousel responsive={responsive} className="container">
{booksData.map((element) => {
return (
<>
<div class="container page-wrapper">
<div class="page-inner">
<div class="row">
<div class="el-wrapper">
<div class="box-up">
<img class="img" src={element.images} alt="" />
<div class="img-info">
<div class="info-inner">
<span class="p-name text-info">
{element.name}
</span>
<span class="p-company ">Author:CHAUDHRY</span>
</div>
<div class="a-size ">
About:This is a complete book on javascript
<span class="size"></span>
</div>
</div>
</div>
<input
type="text"
value={1}
style={{ display: "none" }}
/>
<div class="box-down">
<div class="h-bg">
<div class="h-bg-inner"></div>
</div>
<a class="cart">
<span class="price">{element.price + "$"}</span>
<span
class="add-to-cart btn btn-sm"
style={{ backgroundColor: "#3EC1D5" }}
onClick={()=>{addNow(element)}}
>
<span class="txt">
ADD TO CART <FaShoppingCart />
</span>
</span>
</a>
</div>
</div>
</div>
</div>
</div>
</>
);
})}
</Carousel>
</div>
</>
);
};
export default Books;
and here is my cart file. where i want to perform the action like update and delete.
Cart.js
import React from "react";
import "./components/cart.css";
import { useEffect } from "react";
const Cart = () => {
let data = localStorage.getItem("products");
let javaData = JSON.parse(data);
let removeData = (e) => {
};
useEffect(() => {
localStorage.clear()
}, [])
return (
<>
<div class="container mt-5 mb-5">
<div class="d-flex justify-content-center row">
<div class="col-md-8">
<div class="p-2 shoingTitle">
<h4>Shop Now</h4>
<span class="text-danger">Remove all</span>
</div>
{javaData ? (
javaData.map((item) => {
return (
<>
<div class="d-flex flex-row justify-content-between align-items-center p-2 bg-white mt-4 px-3 rounded">
<div class="mr-1 imageandpara">
<img class="rounded" src={item.images} width="70" />
<span class="font-weight-bold">{item.name}</span>
</div>
<div class="d-flex flex-column align-items-center product-details">
<div class="d-flex flex-row product-desc"></div>
</div>
<div class="d-flex flex-row align-items-center qty">
<i class="minusSign shadow">
<i class="bi bi-dash"></i>
</i>
<span class="text-grey quantityNumber">
{item.quantity}
</span>
<i class="minusSign shadow">
<i class="bi bi-plus"></i>
</i>
</div>
<div>
<span class="text-grey productAmount">{`${
item.quantity * item.price
}$`}</span>
</div>
<div
class="d-flex align-items-center text-dark "
style={{
cursor: "pointer",
fontWeight: "900",
fontSize: "15px",
}}
onClick={() => {
removeData(item);
}}
>
<i class="bi bi-x text-danger"></i>
</div>
</div>
</>
);
})
) : (
<h3 style={{ textAlign: "center" }}>Cart is empety</h3>
)}
<div class="d-flex flex-row align-items-center mt-3 p-2 bg-white rounded">
<input
type="text"
class="form-control gift-card "
placeholder="discount code/gift card"
/>
<button
class="btn btn-sm ml-3 shadow"
type="button"
style={{
outline: "#3EC1D5",
backgroundColor: "#3EC1D5",
color: "white",
}}
>
Apply
</button>
</div>
<div class="totalItems">
Total Items: <strong>12</strong>
</div>
<span class="TotalPrice">
Total price: <strong>12$</strong>
</span>
<div class="d-flex flex-row align-items-center mt-3 p-2 bg-white rounded">
<button
class="btn btn-block btn-sm ml-2 pay-button shadow"
type="button"
style={{ backgroundColor: "#3EC1D5" }}
>
Proceed to Pay
</button>
</div>
</div>
</div>
</div>
</>
);
};
export default Cart;
Try this for add:
let removeData = (e) => {
localStorage.setItem("name of the item") // e.target.name
};
There's alot going on in your site :)
I think it will be responsible to create a context, that will serve the cart to all other components.
Things to note here, (Found some little improvements)
Run the function in the useState hook, don't just leave it there like you did
When using Array.filter you need to return a boolean iorder for it too filter your array properly.
This is the code I brought up hope it helps you out.
CartContext.js file.
import React, { createContext, useContext, useEffect, useState } from "react";
export const CartContext = createContext();
function Cart({ children }) {
const arr = useCallback(() => {
let dat = localStorage.getItem("products");
if (dat) {
return JSON.parse(dat);
} else {
return [];
}
}, []);
const [productData, setproductData] = useState(() => arr());
const getJason = async () => {
try {
let fea = await fetch(
"https://script.google.com/macros/s/AKfycbxFCG7S-kjncQZwvcMnqq4wXoBAX8ecH1zkY2bLP7EE-YHlnKbiJ3RUuHtWLe6sIK30Kw/exec"
);
let acData = await fea.json();
// filter callback function should return a boolean. That is either true or false in order to make it work.
// SO i think this function isn't going to function properly
let itemsData = acData.shop.filter((element) => {
if (element.name) {
return element;
}
});
setbooksData(itemsData);
if (itemsData) {
croser.current.style.filter = "blur(0px)";
loding.current.style.display = "none";
}
} catch (error) {
croser.current.style.filter = "blur(0px)";
loding.current.style.display = "none";
}
};
// get product data from api
useEffect(() => {
getJason();
}, []);
const addProduct = (e) => {
// check if product id available on cart
const findProduct = productData.find((element) => {
return element.id === e.id;
});
// add first quantity if not available
if (!findProduct)
return setproductData([...productData, { ...e, quantity: 1 }]);
// increase quantity by 1
const newCart = productData.map((element) => {
return {
...element,
quantity: element.id === e.id ? element.quantity + 1 : element.quantity,
};
});
setproductData(newCart);
};
const removeProduct = (e) => {
// check if product id available on cart
const findProductQuantity = productData.find((element) => {
return element.id === e.id && element.quantity >= 1;
});
// add first quantity if not available
if (!findProduct)
// Your ui should prevent this
return;
// decrease quantity by 1
const reducedQuantityCart = productData.map((element) => {
return {
...element,
quantity: element.id === e.id ? element.quantity - 1 : element.quantity,
};
});
// filter out all products with quantity less than 1 (quantity : 0)
const newCart = productData.filter((element) => {
return element.quantity >= 1;
});
setproductData(newCart);
};
const deleteProduct = (e) => {
// check if product id available on cart
const findProduct = productData.find((element) => {
return element.id === e.id;
});
// add first quantity if not available
if (!findProduct)
// Your ui should prevent this
return;
const productIndex = productData.findIndex((element) => {
return element.id === e.id;
});
// splice (delete) product from the productData array
const newCart = [productData].splice(productIndex, 1);
setproductData(newCart);
};
const value = {
productData,
addProduct,
removeProduct,
deleteProduct,
};
return <CartContext.Provider value={value}>{children}</CartContext.Provider>;
}
In here you create a context, create all your function you will use to update your cart, and pass them to your Context provider
// create a hook can you use anywhere in the app
export const useCart = () => {
const context = useContext(CartContext);
if (!context) {
throw new Error("Must use useCart in CartContext Child");
}
const { productData, addProduct, removeProduct, deleteProduct } = context;
return { productData, addProduct, removeProduct, deleteProduct };
};
For this you create a custom hook that you can use inside any component provided it is a child of the Cart context. So make sure you wrap it all around your app
// Use Case
const SomeComponent = () => {
const { productData, addProduct, removeProduct } = useCart();
return (
<div>
<p> Number of Products: {productData.length}</p>
<div>
{productData?.map((product) => (
<div>
<p>{product?.name}</p>
<button onClick={() => addProduct(product)}>add</button>
<button onClick={() => removeProduct(product)}>
subtract/reduce
</button>
<button onClick={() => deleteProduct(product)}>delete</button>
</div>
))}
</div>
</div>
);
};
Use case Scenario od how this code will work. Hope you find this helful

How to dynamically fetch api route from component in page folder?

i have a reusable contact form that works perfectly when used in the index.js file.
However when i use it from a component in the page folder i am having a 404 not found error message because it uses this route 3000/ourServices/conciergerie/api/contact/ instead of 3000/api/contact.
How do i ensure the it will always fetch the correct route? please see how i fetch the api below :
async function handleSubmit() {
const data = {
firstName,
email,
phone,
message,
};
const res = await fetch("api/contact", {
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
data: data,
token: "test",
}),
});
alert("Message sent! Thank you\nWe will be in touch with you soon!");
}
pages/ourServices/conciergerie
import Image from "next/image";
import { AiOutlinePlus, AiOutlineMinus } from "react-icons/ai";
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { Contact } from "../../components/contact/Contact";
import es from "../../locales/es-ES/conciergerie.json";
import en from "../../locales/en-US/conciergerie.json";
import Icon1 from "/public/1.svg";
import Icon2 from "/public/2.svg";
import Icon3 from "/public/3.svg";
const Conciergerie = () => {
let { locale } = useRouter();
let t = locale === "es-ES" ? es : en;
// const { t } = useTranslation(locale, "conciergerie");
let myIcons = [Icon1, Icon2, Icon3];
const scrollToConciergerie = () => {
window.scrollTo({
top: 300,
behavior: "smooth",
});
};
const myLoader = ({ src, width, quality }) => {
return `${src}?w=${width}&q=${quality || 75}`;
};
const [showform, setshowform] = useState(false);
useEffect(() => {
window.addEventListener("load", scrollToConciergerie);
return () => {
window.removeEventListener("load", scrollToConciergerie);
};
});
const showContactForm = () => {
return <Contact />;
};
const contentData = t.conciergerieData;
return (
<div className="section" onLoad={scrollToConciergerie}>
<div className="container">
<div className="text-center">
<h1 className=" my-4 text-capitalize" id="conciergerie">
{t.conciergerieHeader}
</h1>
</div>
<h3 className="text-capitalize concierge-subheading mt-3">
{t.conciergerieTitle}
</h3>
<p className="lead concierge-subheading-text">{t.conciergerieText}</p>
</div>
<div className="container">
<div className="row text-center mt-5">
{contentData?.map((item, index) => {
return (
<div className="col-md-4" key={index}>
<span className="fa-stack fa-4x">
<Image
layout="responsive"
src={myIcons[index]}
alt="icons"
className="svg-inline--fa fa-solid fa-stack-1x fa-inverse img-fluid"
aria-hidden="true"
focusable="false"
data-prefix="fas"
data-icon="house"
role="img"
objectFit="cover"
height={300}
width={300}
//loader={myLoader}
/>
</span>
<h4 className="my-3 text-hogar2 text-uppercase">
{item.title}
</h4>
<ul>
{item.text.map((text) => {
return (
<li key={text.id} className="list-unstyled">
<p className="m-0 text-muted text-list">
{text.content}
</p>
</li>
);
})}
</ul>
{item.id === "algomas" &&
(!showform ? (
<AiOutlinePlus
role="button"
onClick={() => {
setshowform(!showform);
}}
className="fs-2"
fill="#5ab4ab"
/>
) : (
<AiOutlineMinus
role="button"
onClick={() => {
setshowform(!showform);
}}
className="fs-2"
fill="#5ab4ab"
/>
))}
{item.id === "else" &&
(!showform ? (
<AiOutlinePlus
role="button"
onClick={() => {
setshowform(!showform);
}}
className="fs-2"
fill="#5ab4ab"
/>
) : (
<AiOutlineMinus
role="button"
onClick={() => {
setshowform(!showform);
}}
className="fs-2"
fill="#5ab4ab"
/>
))}
</div>
);
})}
</div>
{showform && showContactForm()}
</div>
</div>
);
};
export default Conciergerie;
can someone help me please?
The reason this problem is happening has to do with absolute and relative paths.
fetch("api/contact")
Is a relative path. The fetch function figures out the path of the current file, ie 3000/ourServices/conciergerie, and adds api/contact to it
On the other hand, if you add a "/" before the path :
fetch("/api/contact")
Fetch figures out the root path of the project, then adds the path you added, ie :
3000/api/contact
TL;DR: Change fetch("api/contact") to fetch("/api/contact").

Cannot add item to cart when click on the button

When i click on the cart symbol, it changes its value to be in cart but actually my product is not rendered on the cart component, is there something wrong or missing in my code, please help me to fix it! Thank you so much!
Context.js:
class ProductProvider extends React.Component {
state = {
products: storeProducts,
detailProduct: detailProduct,
cart: [],
modalOpen: false,
modalProduct: detailProduct
};
getItem = (id) => {
const product = this.state.products.find((item) => item.id === id);
return product;
};
addToCart = (id) => {
console.log("add to cart");
let tempProducts = [...this.state.products];
const index = tempProducts.indexOf(this.getItem(id));
const product = tempProducts[index];
product.inCart = true;
product.count = 1;
const price = product.price;
product.total = price;
this.setState(() => {
return (
{ products: tempProducts, cart: [...this.state.cart, product] },
() => console.log(this.state)
);
});
};
openModal = (id) => {
const product = this.getItem(id);
this.setState(() => {
return { modalProduct: product, openModal: true };
});
};
closeModal = (id) => {
this.setState(() => {
return { modalOpen: false };
});
};
Cart.js:
import React from "react";
import CartColumns from "./CartColumns";
import CartList from "./CartList";
import EmptyCart from "./EmptyCart";
import { ProductContext } from "../App";
export default class Cart extends React.Component {
render() {
return (
<div>
<ProductContext.Consumer>
{(value) => {
console.log(value, "inside Product COnt");
if (value.length > 0) {
return (
<div>
<CartColumns />
<CartList items={items} />
</div>
);
} else {
<EmptyCart />;
}
}}
</ProductContext.Consumer>
</div>
);
}
}
CartList.js:
import React from "react";
import CartItem from "./CartItem";
export default function CartList(props) {
const { items } = props;
return (
<div>
{items.cart.map((item) => (
<CartItem
key={item.id}
item={item}
increment={item.increment}
decrement={item.decrement}
/>
))}
</div>
);
}
CartItem.js:
import React from "react";
function CartItem(props) {
const { id, title, img, price, total, count } = props.item;
const { increment, decrement, removeItem } = props;
return (
<div className="row my-1 text-capitalize text-center">
<div className="col-10 mx-auto col-lg-2">
<img
src={img}
style={{ width: "5rem", heigth: "5rem" }}
className="img-fluid"
alt=""
/>
</div>
<div className="col-10 mx-auto col-lg-2 ">
<span className="d-lg-none">product :</span> {title}
</div>
<div className="col-10 mx-auto col-lg-2 ">
<strong>
<span className="d-lg-none">price :</span> ${price}
</strong>
</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">{count}</span>
<span className="btn btn-black mx-1">+</span>
</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 ">
<strong>item total : ${total} </strong>
</div>
</div>
);
}
export default CartItem;
Sandbox link for better observation:https://codesandbox.io/s/cart-code-addict-forked-l6tfm?file=/src/cart/CartItem.js
There were few issues
Change following
this.setState(() => {
return (
{ products: tempProducts, cart: [...this.state.cart, product] },
() => console.log(this.state)
);
});
to
this.setState(
{
products: tempProducts,
cart: [...this.state.cart, product]
},
() => console.log(this.state)
);
And need to use value.cart to check for length and when passing as a prop.
Instead of
if (value.length > 0) {
return (
<div>
<CartColumns />
<CartList items={items} />
</div>
);
}
It should be
if (value.cart.length > 0) {
return (
<div>
<CartColumns />
<CartList items={value.cart} />
</div>
);
}
And in CartList,
Instead of
{
items.cart.map(item => (
<CartItem
key={item.id}
item={item}
increment={item.increment}
decrement={item.decrement}
/>
));
}
it should be
{
items.map(item => (
<CartItem
key={item.id}
item={item}
increment={item.increment}
decrement={item.decrement}
/>
));
}
Now it shows the cart with items
Code sandbox => https://codesandbox.io/s/cart-code-addict-forked-c3kug?file=/src/cart/CartList.js

Event firing twice react stateless component

I have created two components one Stateful(CurrentProjects) and other stateless(PopUpPanel). In the stateless component I had used Office UI Fabric react component Panel.
Panel has a property OnDismiss which triggers when close icon on the panel is clicked. I am passing function as property through CurrentProject component to PopUpPanel component. But when I click on close icon, the function on CurrentProject component is getting fired twice.
So my question is why it is calling it twice. My code is as below
CurrentProjects Component
import * as React from 'react';
import {
DocumentCard,
DocumentCardActions,
DocumentCardActivity,
DocumentCardLocation,
DocumentCardPreview,
DocumentCardTitle,
IDocumentCardPreviewProps,
IDocumentCardActivityProps,
Panel,
PanelType,
Image,
Persona,
IPersonaProps,
PersonaSize
} from 'office-ui-fabric-react';
import styles from './CurrentProjects.module.scss';
import { ICurrentProjectsProps } from './ICurrentProjectsProps';
import { ICurrentProjectsState } from './ICurrentProjectsState';
import { escape, } from '#microsoft/sp-lodash-subset';
import { ImageFit } from 'office-ui-fabric-react/lib/Image';
import { TestImages } from "../../../../common/TestImages";
import { IProject } from '../IProject';
import PopUpPanel from "../PopUpPanel/PopUpPanel";
import IPopUpPanelProps from "../PopUpPanel/IPopUpPanelProps";
const img = require('../../../../images/avatar-kat.png');
export default class CurrentProjects extends React.Component<ICurrentProjectsProps, ICurrentProjectsState> {
public constructor(props:ICurrentProjectsProps){
super(props);
this.state = {
showPanel:false,
currentProject: null
};
this.onDismiss = this.onDismiss.bind(this);
}
private onCardClick(id:number, event:React.SyntheticEvent<HTMLElement>):void{
//console.log("event=>",event.currentTarget);
//console.log("Id=>",id);
//debugger;
this.setState((prevState:ICurrentProjectsState)=>{
const project = this.props.projects.filter(proj=>proj.ID===id)[0];
return {
showPanel: !prevState.showPanel,
currentProject: project
};
});
}
private onDismiss():void{
//debugger;
this.setState((prevState:ICurrentProjectsState)=> {
return {
showPanel: false //!prevState.showPanel
};
});
}
public render(): JSX.Element {
const project:IProject = this.state.currentProject;
const previewProps: IDocumentCardPreviewProps = {
previewImages: [
{
name: 'My Project',
previewImageSrc: TestImages.documentPreview,
imageFit: ImageFit.contain,
width: 250,
height: 100
},
],
};
const element:JSX.Element[] = this.props.projects === undefined ? []: this.props.projects.map((project,index) => {
const style = {
width: project.PercentageComplete + "%",
};
let dueDate: string = "";
if(project.DueDate)
{
const dueDateObj = new Date(project.DueDate.toString());
dueDate = dueDateObj.toLocaleDateString('en-gb',{year: '2-digit', month: 'short', day: 'numeric'});
}
return (
<DocumentCard className={styles.myCard} key={project.ID} onClick={this.onCardClick.bind(this,project.ID)}>
<div className={styles.priorityDiv + " " + styles[this.getProjectPriorityClassName(project.Priority)]}>
<DocumentCardPreview { ...previewProps } />
<div className={styles.projectDetails}>
<DocumentCardTitle
title={project.Client}
shouldTruncate={ false }
showAsSecondaryTitle={true}
/>
<div className={styles.detailsDiv + ' ms-fontSize-mi'}><span className='ms-font-mi'>Title:</span> {project.Title}</div>
<div className={styles.detailsDiv + ' ms-fontSize-mi'}><span className='ms-font-mi'>BU:</span> {project.BusinessUnit}</div>
<div className={styles.detailsDiv + ' ms-fontSize-mi'}><span className='ms-font-mi'>Due Date:</span> {dueDate}</div>
<div className={styles.seperatorDiv}>
{/* <hr/> */}
<div className={styles.progressBarDiv}><div style={style}></div></div>
<span>{project.PercentageComplete}%</span>
</div>
<div className={styles.detailsDiv + ' ms-fontSize-mi'}>{project.RequestType}</div>
</div>
</div>
</DocumentCard>
);
});
const popUpPanelProps:IPopUpPanelProps = {
project: project,
showPanel: this.state.showPanel,
//onDismiss: this.onDismiss
};
return (
<div className={styles.currentProjects}>
{element}
<PopUpPanel {...popUpPanelProps} onDismiss={this.onDismiss} />
</div>
);
}
private getProjectPriorityClassName(priority:string): string {
switch(priority)
{
case "Low" :
return "priorityLow";
case "Medium" :
case "Moderate" :
return "priorityMedium";
case "High" :
return "priorityHigh";
default:
return "";
}
}
private getProgressBarColor(progress:number): string {
if(progress <= 30) {
return "red";
} else if(progress >30 && progress <=70) {
return "orange";
} else if(progress >70) {
return "green";
}
}
}
PopUpPanel Component
import * as React from 'react';
import {
Panel,
PanelType,
Image,
Persona,
IPersonaProps,
PersonaSize
} from 'office-ui-fabric-react';
import styles from '../ProjectCardBoard/CurrentProjects.module.scss';
import IPopUpPanelProps from './IPopUpPanelProps';
import { escape, } from '#microsoft/sp-lodash-subset';
import { ImageFit } from 'office-ui-fabric-react/lib/Image';
import { TestImages } from "../../../../common/TestImages";
import { IProject } from '../IProject';
const img = require('../../../../images/avatar-kat.png');
const PopUpPanel: React.SFC<IPopUpPanelProps> = (props:IPopUpPanelProps) => {
if(!props.project)
return null;
const project = props.project;
// const onDismiss = ()=> {
// this.props.onDismiss();
// };
return (<Panel
isOpen={ props.showPanel }
type={ PanelType.smallFluid }
onDismiss={ props.onDismiss }>
<div className={styles.ProjectDetailsPanel + " ms-Grid"}>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-md3 ms-lg2 ms-xl2 ms-xxl2 ms-xxxl1">
<Image src={String(img)}
imageFit={ImageFit.cover}
className={styles.ProjectIconDP}
alt='Some Alt' />
</div>
<div className="ms-Grid-col ms-md6 ms-lg8 ms-xl7 ms-xxl8 ms-xxxl9">
<p className="ms-fontSize-s ms-fontWeight-semibold">{project.Client}</p>
<p className="ms-fontSize-mi">{project.Title} <span className="ms-fontSize-s"> | </span> {project.BusinessUnit} <span className="ms-fontSize-s"> | </span>{project.RequestType}</p>
</div>
<div className="ms-Grid-col ms-md3 ms-lg2 ms-xl3 ms-xxl2 ms-xxxl2">
<p>Total Contract Value</p>
<p><strong>{project.TotalContractValue}</strong></p>
</div>
</div>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-sm12 ms-md12">
<h3>{"Bid Team".toUpperCase()}</h3>
<div className={styles.lineSeperator}></div>
</div>
</div>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-mdPush5 ms-md2 ms-lg2 ms-xl1 ms-textAlignCenter">
<div className={styles.peopleHeaderDiv}>
<h3 className={styles.peopleHeading}>{"Core Team".toUpperCase()}</h3>
</div>
{project.CoreTeam && project.CoreTeam.map((member)=>{
const personaProps:IPersonaProps = {
imageUrl: member.ImgSrc,
imageInitials: member.Title.split(" ").reduce((prevVal,currentVal,index)=>{
return prevVal += currentVal.charAt(0);
},""),
primaryText: member.Title,
secondaryText: member.Designation,
tertiaryText: member.PhoneNo,
size:PersonaSize.size72
};
return <Persona {...personaProps} />;
})}
<div className={styles.peopleHeaderDiv}>
<h3 className={styles.peopleHeading}>{"Contributors".toUpperCase()}</h3>
</div>
{project.Contributors && project.Contributors.map((member)=>{
const personaProps:IPersonaProps = {
imageUrl: member.ImgSrc,
imageInitials: member.Title.split(" ").reduce((prevVal,currentVal,index)=>{
return prevVal += currentVal.charAt(0);
},""),
primaryText: member.Title,
secondaryText: member.Designation,
tertiaryText: member.PhoneNo,
size:PersonaSize.size72
};
return <Persona {...personaProps} />;
})}
</div>
</div>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-sm12 ms-md12">
<h3>{"Reason For Status".toUpperCase()}</h3>
<div className={styles.lineSeperator}></div>
</div>
</div>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-sm12 ms-md12"
dangerouslySetInnerHTML={{__html:project.ReasonForStatus}} />
</div>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-sm12 ms-md12">
<h3>{"Win Strategy".toUpperCase()}</h3>
<div className={styles.lineSeperator}></div>
</div>
</div>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-sm12 ms-md12"
dangerouslySetInnerHTML={{__html:project.WinStrategy}} />
</div>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-sm12 ms-md12">
<h3>{"Key Actions".toUpperCase()}</h3>
<div className={styles.lineSeperator}></div>
</div>
</div>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-sm12 ms-md12"
dangerouslySetInnerHTML={{__html:project.KeyActionsOrNextSteps}} />
</div>
</div>
</Panel>);
};
export default PopUpPanel;
NOTE: If I change line if(!props.project) to if(!props.project || !props.showPanel) in PopupPanel component than event is triggered only once.

Resources