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

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").

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 save image uploaded from firebase storage to currentUser in firestore database

I'm unsure of how to get the image url that was uploaded to firebase storage and save it to the firestore database. Currently i have a Profile component that stores the logic for a user to upload a photo to firebase storage. I have a UserContext file which listens for user changes. As-well as a Firebase.utils which creates the user.
Firebase.utils file:
import firebase from 'firebase/compat/app';
import 'firebase/compat/firestore';
import 'firebase/compat/auth';
import "firebase/compat/storage";
const config = {
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: "",
measurementId: ""
};
firebase.initializeApp(config);
export const createUserProfileDocument = async (userAuth, additionalData) => {
// If user is not signed in do nothing
if (!userAuth) return;
const userRef = firestore.doc(`users/${userAuth.uid}`)
const snapShot = await userRef.get()
if (!snapShot.exists) {
// Added photoURL ###
const { displayName, email, photoURL } = userAuth;
const createdAt = new Date();
try {
await userRef.set({
displayName,
email,
createdAt,
photoURL,
...additionalData
})
} catch (error) {
console.log('error creating user', error.message)
}
}
return userRef;
}
export const auth = firebase.auth();
export const firestore = firebase.firestore();
const storage = firebase.storage();;
export { storage, firebase as default };
The UserContext file:
import React, { useContext, useState, useEffect } from 'react';
import { auth, createUserProfileDocument } from "../Firebase/Firebase.utils";
const UserContext = React.createContext(null);
const UserUpdateContext = React.createContext();
const UserUpdateNameContext = React.createContext();
const UserUpdateEmailContext = React.createContext();
export const useUserContext = () => {
// useContext hook
return useContext(UserContext);
}
export const useUserContextUpdate = () => {
// useContext hook - toggleUser signout function
return useContext(UserUpdateContext)
}
export const useUserNameUpdate = () => {
// useContext hook - update user displayName
return useContext(UserUpdateNameContext)
}
export const useUserEmailUpdate = () => {
// useContext hook - update user email
return useContext(UserUpdateEmailContext)
}
export const UserContextProvider = ({ children }) => {
const [currentUser, setUser] = useState(null);
let unsubscribeFromAuth = null;
console.log(currentUser)
useEffect(() => {
unsubscribeFromAuth = auth.onAuthStateChanged(async userAuth => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
userRef.onSnapshot(snapShot => {
setUser({
id: snapShot.id,
...snapShot.data()
});
});
} else {
setUser(null)
// setUser({ currentUser: userAuth }) OBJECTS ARE TRUTHY
}
});
return () => {
unsubscribeFromAuth();
};
}, [])
console.log(unsubscribeFromAuth)
const toggleUser = () => {
auth.signOut()
.then(() => {
setUser(null)
})
.catch(e => console.log('There was a error:'(e)))
}
// console.log(currentUser)
// Get current window width
const useWindowWidth = () => {
const [width, setWidth] = useState(window.innerWidth)
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth)
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
}
})
return width
}
const width = useWindowWidth();
// Slice off end of displayName if reaches a certain length
const sliceDisplayName = (currentUser) => {
if (currentUser) {
const displayName = currentUser.displayName;
return (
width >= 1441 ? displayName.substring(0, 16) + '...'
: width <= 1440 && width >= 769 ? displayName.substring(0, 14) + '...'
: width <= 768 ? displayName.substring(0, 7) + '...'
: displayName
)
} else (console.log("No user found :("))
}
// console.log(sliceDisplayName(currentUser))
// Slice off end of email if reaches a certain length
const sliceEmail = (currentUser) => {
if (currentUser) {
const email = currentUser.email;
return (
width >= 1441 ? email.substring(0, 16) + '...'
: width <= 1440 && width >= 769 ? email.substring(0, 14) + '...'
: width <= 768 ? email.substring(0, 7) + '...'
: email
)
} else (console.log("No user found :("))
}
// console.log(sliceEmail(currentUser))
return (
<UserContext.Provider value={currentUser} >
<UserUpdateContext.Provider value={toggleUser} >
<UserUpdateNameContext.Provider value={sliceDisplayName} >
<UserUpdateEmailContext.Provider value={sliceEmail} >
{children}
</UserUpdateEmailContext.Provider >
</UserUpdateNameContext.Provider >
</UserUpdateContext.Provider >
</UserContext.Provider >
)
};
The Profile Component:
import React, { useState } from 'react';
import ReactTooltip from 'react-tooltip';
import { useUserContext, useUserContextUpdate, useUserNameUpdate } from '../../Utilities/Context/UserContext';
import { storage } from "../../Utilities/Firebase/Firebase.utils";
import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
import ActivityFeed from '../../../src/components/ActivityFeed/ActivityFeed';
import Post from '../../../src/components/Post/Post';
import './Profile.css';
const Profile = ({ imageDate }) => {
const currentUser = useUserContext(); // Current user
const sliceDisplayName = useUserNameUpdate(); // Window width < (width) ? update displayName length
const [image, setImage] = useState("");
const [url, setUrl] = useState("");
// Listen for state changes, errors, and completion of the upload.
const handleUpload = () => {
const uploadTask = storage.ref(`images/${image.name}`).put(image);
uploadTask.on('state_changed',
(snapshot) => { console.log(snapshot) },
(error) => {
switch (error.code) {
case 'storage/unauthorized':
break;
case 'storage/canceled':
break;
case 'storage/unknown':
break;
}
},
(e) => {
storage
.ref("images")
.child(image.name)
.getDownloadURL(uploadTask.snapshot.ref)
.then(url => {
setUrl(url);
console.log('File available at', url);
})
}
);
}
console.log("image: ", image);
console.log(image.lastModifiedDate) // Image date uploaded. image.lastmodifiedDate === undefined ?
console.log(url)
const handleUploadChange = e => { // Maybe inside this function i can do the logic for recent activity and 0 photos +1
if (e.target.files[0]) {
setImage(e.target.files[0]);
}
};
return (
<div className="container-flex">
<div className="top-content-container w-100">
<div className="bot-content-wrapper px-1 py-2 mx-lg-auto d-flex flex-column flex-lg-row">
<div className="w-spacer profile-image-wrapper position-relative">
{
currentUser ?
<input
type="file"
for="Upload Image"
accept="image/*"
name="image"
id="file"
onChange={handleUploadChange}
onClick={handleUploadChange}
style={{ display: "none" }}
/>
: ''
}
<label for="file">
{
url.length <= 0 ?
<img
id='myimg'
className="profile-image-default profile-image d-block"
alt=""
/>
: currentUser ?
<>
<img
id='myimg'
className="profile-image d-block"
src={url}
alt=""
data-tip="Click me to update profile picture!" />
<ReactTooltip place="top" type="dark" effect="float" />
</>
:
<img
id='myimg'
className="profile-image-default profile-image d-block"
alt=""
/>
}
</label>
</div>
<div className="d-flex flex-column flex-lg-row align-items-lg-center w-lg-75 m-l-4">
<div className="d-flex flex-column flex-lg-row ml-auto pr-1 m-r-md-vw">
<div className="m-r-md">
<div className="d-flex flex-column w-100 m-r-6">
<div>
{
currentUser ?
<h2
data-tip={currentUser.displayName}>
{sliceDisplayName(currentUser)}
<span><ReactTooltip place="top" type="dark" effect="float" /></span>
</h2>
:
<h2>No User</h2>
}
</div>
<div className="d-flex flex-column flex-lg-row">
<div className="">
<i className="bi bi-people"></i>
<span className="banner-list-font mx-1">0 friends</span>
</div>
<div className="mx-lg-2">
<i className="bi bi-star"></i>
<span className="banner-list-font mx-1">0 reviews</span>
</div>
<div className="">
<i className="bi bi-camera"></i>
<span className="banner-list-font mx-1">0 photos</span>
</div>
</div>
</div>
</div>
<hr className=" d-lg-none" style={{ color: '#0a0a0a' }}></hr>
<div className="ml-3">
<div className="update-profile-wrapper grey-line-break d-flex flex-column m-l">
<div className="">
{
image.name !== undefined ?
<button
className="banner-list-font"
onClick={handleUpload}
// onClick={() => {
// handleUpload();
// forceUpdate();
// }}
>Add Profile Photo
</button>
: ''
}
</div>
<div className="">
<a className="banner-list-font" href='#'>Update Your Profile</a>
</div>
<div className="">
<a className="banner-list-font" href='#'>Find Friends</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div className="bot-content-container px-1 py-4 custom-padding">
<div className="bot-content-wrapper mx-lg-auto d-flex flex-column flex-lg-row">
<div className="sidebar d-flex flex-column mx-auto mx-lg-0 mt-lg-5 py-lg-2 px-2">
{
currentUser ?
<h4 className="mb-3">{currentUser.displayName}</h4>
:
<h4 className="mb-3">No User</h4>
}
<ul className="p-0">
<a className="cursor-pointer text-decoration-none">
<li className="d-flex flex-row sidebar-item sidebar-list-font">
<i className="mx-2 bi bi-person-badge"></i>
<h5 className="sidebar-list-font">Overview</h5>
</li>
<hr style={{ color: '#0a0a0a' }}></hr>
</a>
<a className="cursor-pointer text-decoration-none">
<li className="d-flex flex-row sidebar-item sidebar-list-font">
<i className="mx-2 bi bi-person-plus"></i>
<h5 className="sidebar-list-font">Friends</h5>
</li>
<hr style={{ color: '#0a0a0a' }}></hr>
</a>
<a className="cursor-pointer text-decoration-none">
<li className="d-flex flex-row sidebar-item sidebar-list-font">
<i className="mx-2 bi bi-award mx-1"></i>
<h5 className="sidebar-list-font">Reviews</h5>
</li>
<hr style={{ color: '#0a0a0a' }}></hr>
</a>
<a className="cursor-pointer text-decoration-none">
<li className="d-flex flex-row sidebar-item sidebar-list-font">
<i className="mx-2 bi bi-lightbulb"></i>
<h5 className="sidebar-list-font">Tips</h5>
</li>
<hr style={{ color: '#0a0a0a' }}></hr>
</a>
<a className="cursor-pointer text-decoration-none">
<li className="d-flex flex-row sidebar-item sidebar-list-font">
<i className="mx-2 bi bi-bookmark-star"></i>
<h5 className="sidebar-list-font">Bookmarks</h5>
</li>
<hr style={{ color: '#0a0a0a' }}></hr>
</a>
<a className="cursor-pointer text-decoration-none">
<li className="d-flex flex-row sidebar-item sidebar-list-font">
<i className="mx-2 bi bi-bookmarks"></i>
<h5 className="sidebar-list-font">Collections</h5>
</li>
<hr style={{ color: '#0a0a0a' }}></hr>
</a>
<a className="cursor-pointer text-decoration-none">
<li className="d-flex flex-row sidebar-item sidebar-list-font">
<i className="mx-2 bi bi-calendar-check"></i>
<h5 className="sidebar-list-font">Events</h5>
</li>
<hr style={{ color: '#0a0a0a' }}></hr>
</a>
<a className="cursor-pointer text-decoration-none">
<li className="d-flex flex-row sidebar-item sidebar-list-font">
<i className="mx-2 bi bi-clock-history"></i>
<h5 className="sidebar-list-font">Order History</h5>
</li>
<hr style={{ color: '#0a0a0a' }}></hr>
</a>
</ul>
</div>
<div className="d-flex flex-column flex-lg-row w-100-md w-75-lg p-3 p-lg-0 m-l-4 pt-lg-3 pt-xl-4">
<div className="activity m-l-3">
<h3 className="heading-red">Notifications</h3>
<p className="font-14">No new friend requests or compliments at this time.</p>
<hr className="d-none d-lg-block" style={{ color: '#0a0a0a' }}></hr>
<h3 className="heading-red">Recent Activity</h3>
{<ActivityFeed />}
{<Post />}
</div>
<hr className="d-lg-none" style={{ color: '#0a0a0a' }}></hr>
<div className="grey-line-break ml-3">
<h3 className="heading-red mb-1 break-word">About
{
currentUser ?
<h3
data-tip={currentUser.displayName}
className="heading-red mb-1">
{sliceDisplayName(currentUser)}
<span><ReactTooltip place="top" type="dark" effect="float" /></span>
</h3>
:
<h3 className="heading-red mb-1">No User</h3>
}
</h3>
<h5 className="about-subHeading mt-2">Yelping Since</h5>
<p className="font-14">Some month</p>
<h5 className="about-subHeading mt-2">Things I Love</h5>
<p className="font-14">You haven't said yet...</p>
</div>
</div>
</div>
</div>
</div>
);
};
export default Profile;
**EDIT**
Updated uploadTask
import { createUserProfileDocument } from "../../Utilities/Firebase/Firebase.utils";
() => {
storage
.ref('images/')
.child(image.name)
.getDownloadURL(uploadTask.snapshot.ref)
.then(async (url, firestore, userAuth) => {
setUrl(url);
const userRef = firestore.doc(`users/${userAuth.uid}`)
await userRef.update({
photoURL: url
});
console.log('File available at', url);
})
}
Error after update:
Unhandled Rejection (TypeError): Cannot read properties of undefined (reading 'doc')
**EDIT UPDATE #2**
It seems that ive managed to get the uploaded photo from storage to go into the firestore database thanks to the help of Frank van Puffelen. I can see it in console.log of current user in the photoURL and its showing in the firebase db user collection as-well. But if i refresh or go to another page the image goes away, even though its still showing the url in the currentUser console.log even after the refresh. Why would that be happening? It must be because the setUrl(url) reinitializes state of the url image back to nothing on every render. I should be good to ditch this all together and call the image url directly from the currentUser like: currentUser.photoURL
Updated code:
import { storage, firestore } from "../../Utilities/Firebase/Firebase.utils";
() => {
storage
.ref('images/')
.child(image.name)
.getDownloadURL(uploadTask.snapshot.ref)
.then(async (url) => {
setUrl(url);
const userRef = firestore.doc(`users/${currentUser.id}`)
await userRef.update({
photoURL: url
});
console.log('File available at', url);
})
}
As far as I can see, you upload the image and get its download URL in this snippet:
storage
.ref("images")
.child(image.name)
.getDownloadURL(uploadTask.snapshot.ref)
.then(url => {
setUrl(url);
console.log('File available at', url);
})
To also write the new URL to the user's profile document in Firestore, add this to the then() callback:
const userRef = firestore.doc(`users/${userAuth.uid}`)
await userRef.update({
photoURL: url
});

routing on click and pass a data also though routing : react js

i am tryin to route to localhost/detiled on click on the <li>
when i type in url localhost/detiled my <InnerDetail /> is loading i want the same when i click on the <li> tag
and also how can i access suggestion.id in <InnerDetail />
search.js
<Link to={{ pathname:"/detiled" }}
>
<li
style={styles.listyle}
// onMouseOver={{ background: "yellow" }}
key={index}
className={classname}
onClick={finddoctor(suggestion.id)}
>
{suggestion.firstname}
</li>
</Link>
in my path.js i have this
path.js
<Route path="/detiled">
<InnerDetail />
</Route>
import PersonPhoto from "../img/doctor.png";
import { useLocation } from "react-router-dom";
import axios from "axios";
import React, { useState, useEffect } from "react";
export default function Detail(props) {
const location = useLocation();
const [detail, setDetail] = useState(false);
//const data3 = location.state.data;
//const doctor_id = data3.id;
const inipath = window.location.pathname;
const path = inipath.split("/cdoctor/");
const [data3, setdata3] = useState([]);
console.log(path[1]);
useEffect(() => {
const config = {
headers: {
Authorization: `token ` + localStorage.getItem("token"),
},
};
//remove this date after setting up the admin pannel
axios
.get(
"doctor-filter/?id=" + path[1],
config
// config
)
.then((res) => {
console.log(res.data);
// setdata3(res.data);
});
});
return (
<>
{/* {detail ? (
<InnerDetail />
) : (
<> */}
<h4 style={{ textAlign: "left", marginLeft: "10px" }}>
Top Specialities <i className="fa fa-angle-right"></i> /doctor/Detail
</h4>
<hr
style={{
margin: "30px 10px",
background: "#fff",
height: "1px",
border: "none",
}}
/>
<div style={styles.wrapper}>
{/* begin header */}
<div style={styles.pheader}>
<div>
<div style={styles.pname}>
<strong style={styles.namealigner}>Dr {data3.firstname}</strong>
<br />
<strong style={styles.namealigner}> {data3.lastname}</strong>
</div>
<hr style={styles.hr} />
<div style={{ textAlign: "left", fontSize: "12px" }}>
<span>
{" "}
{data3.speciality} | {data3.experience} years of Experience
</span>
</div>
<hr style={styles.hr} />
</div>
<div>
<img style={{ height: "100px" }} src={PersonPhoto} alt="" />
</div>
</div>
{/* end header */}
{/* begin detail */}
<div style={styles.iflex}>
<div style={styles.innerflex}>
<i className="fa fa-graduation-cap"></i>
<strong> Education</strong>
<br />
<small> {data3.qualification}</small>
</div>
<div style={styles.innerflex}>
<i className="fa fa-map-marker"></i>
<strong> Location</strong>
<br />
<small>{data3.location}</small>
</div>
</div>
<div style={styles.iflex}>
<div style={styles.innerflex}>
<i className="fa fa-user"></i>
<strong> Registeration Number</strong>
<br />
<small>{data3.councilRegNo}</small>
</div>
<div style={styles.innerflex}>
<i className="fa fa-globe"></i>
<strong> Language</strong>
<br />
<small>English Malayalam</small>
</div>
</div>
{/* end detail */}
</div>
</>
// )}
// </>
);
}
this is the dashbord here serch is common in all pages
<Search />
<Specialities />
import React from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faSearch } from "#fortawesome/free-solid-svg-icons";
import { useState, useEffect } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
import { useHistory } from "react-router-dom";
const initialState = {
idaddProducts: "",
};
const Searchclients = () => {
const history = useHistory();
const [showResults, setShowResults] = React.useState(true);
const [poName, pnName] = React.useState(initialState);
const [showSerch, setShowSerch] = React.useState([]);
const [detail, setDetail] = useState(false);
const [inputValue, setInputValue] = React.useState("");
const [filteredSuggestions, setFilteredSuggestions] = React.useState([]);
const [selectedSuggestion, setSelectedSuggestion] = React.useState(0);
const [displaySuggestions, setDisplaySuggestions] = React.useState(false);
function finddoctor(e) {
console.log(e);
setDetail(true);
}
const suggestions = [];
showSerch.forEach(function (data) {
suggestions.push(data);
});
const onChange = (event) => {
const value = event.target.value;
setInputValue(value);
setShowResults(false);
//console.log(strung.substring(1, strung.length - 1));
// console.log(JSON.stringify(suggestions));
// var suggestions = suggestions.substring(1, suggestions.length - 1);
// newObj = suggestions;
//console.log(suggestions);
//setFilteredSuggestions({ ...poName, idAddProducts: idAddProducts });
const filteredSuggestions = suggestions.filter(
(suggestion) =>
suggestion.firstname
.toString()
.toLowerCase()
.includes(value.toLowerCase()) ||
suggestion.id.toString().toLowerCase().includes(value.toLowerCase())
);
// const filteredSuggestions = suggestions.filter((suggestion) =>
// suggestion.toString().toLowerCase().includes(value.toLowerCase())
// );
setFilteredSuggestions(filteredSuggestions);
setDisplaySuggestions(true);
};
const onSelectSuggestion = (index) => {
setSelectedSuggestion(index);
setInputValue(filteredSuggestions[index]);
setFilteredSuggestions([]);
setDisplaySuggestions(false);
};
const SuggestionsList = (props) => {
// console.log(props);
const {
suggestions,
inputValue,
onSelectSuggestion,
displaySuggestions,
selectedSuggestion,
} = props;
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list" style={styles.ulstyle}>
{suggestions.map((suggestion, index) => {
// console.log(suggestions);
const isSelected = selectedSuggestion === index;
const classname = `suggestion ${isSelected ? "selected" : ""}`;
return (
<Link to={`/detiled/${suggestion.id}`}> //this isthe link
<li
style={styles.listyle}
// onMouseOver={{ background: "yellow" }}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</Link>
);
})}
</ul>
);
} else {
return <div>No suggestions available...</div>;
}
}
return <></>;
};
useEffect(() => {
axios
.get("all-doctors-list/")
.then((res) => {
const data = res.data;
// pnName(data.data);
// var stringdata = data;
setShowSerch(data);
//console.log(stringdata);
});
// setShowSerch(data);
}, []);
return (
<>
<div className="note-container" style={styles.card}>
<div style={styles.inner}>
<p style={{ textAlign: "left" }}>Search Doctors</p>
<form className="search-form" style={{}}>
{showResults ? (
<FontAwesomeIcon
style={{ marginRight: "-23px" }}
icon={faSearch}
/>
) : null}
<input
onChange={onChange}
value={inputValue}
style={styles.input}
type="Search"
/>
<SuggestionsList
onClick={() => this.nextPath("/detiled")}
inputValue={inputValue}
selectedSuggestion={selectedSuggestion}
onSelectSuggestion={onSelectSuggestion}
displaySuggestions={displaySuggestions}
suggestions={filteredSuggestions}
/>
</form>
</div>
</div>
</>
);
};
export default Searchclients;
You can add a route for detail view of the suggestion.
<Switch>
<Route path="/detailed" exact>
list suggestions component
</Route>
<Route path="/detailed/:id" exact}>
DetailComponent
</Route>
</Switch>
Then the link will become:
<Link to={`/detailed/${suggestion.id}`}>
<li
style={styles.listyle}
// onMouseOver={{ background: "yellow" }}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</Link>
And in DetailComponent you can get the id from route params.
import { useParams } from "react-router-dom";
...
const { id } = useParams(); // the id passed in url

Toggle specific div (id) within a react component

I have a site built with post-components to show articles in a feed. Inside the component, I have a button that opens a modal onClick. I use useState to toggle on the modal which works perfectly fine. The problem is that since the toggle is put on the modal-div inside the component.. every single post modal opens whenever I click one of the buttons. I want to open only the targeted post modal (with the sam post id as the button I’m clicking). I can’t figure out how to do this…in react.
const [toggle, setToggle] = useState (true);
const toggler = () => {
setToggle(prev => !prev)
}
...
return (
<section className="posts">
{data.allMarkdownRemark.edges.map((edge) => {
return (
<div className="post">
<div className="postDescrip">
<h2 className="postTitle">{edge.node.frontmatter.title}</h2>
<h2 className="name">{edge.node.frontmatter.name}</h2>
<button className="readMoreBtn" onClick={toggler}>{toggle ? <h2 className="readMore">Read more</h2> : <h2 className="readMore">Read less</h2>}
</button>
</div>
<Img className="postImg" fluid={edge.node.frontmatter.featuredImage.childImageSharp.fluid} />
<div className={toggle ? 'hide' : 'postCopy'} >
<Close close={toggler} />
<h3>{edge.node.frontmatter.details}</h3>
<div className="copy" dangerouslySetInnerHTML= {{__html: edge.node.html}}></div>
<h4>Read the full article in Issue One</h4>
</div>
</div>
)}
)}
</section>
)
}
export default Posts;
After trying suggested solution using object instead on bolean. I now receive this error message
[Error message][1]for the following code:
const [toggles, setToggles] = useState ({});
let id;
const createToggler = (id) = () => {
setToggles(prev => { [id] : !prev[id] })
// setToggle(prev => { ...prev, [id]: !prev[id] }) // or support multi modal at same time. but I think you don't want it.
}
const data = useStaticQuery(graphql`
query {
allMarkdownRemark (
sort: { order: DESC, fields: [frontmatter___date] }
){
edges {
node {
frontmatter {
id
title
name
details
featuredImage {
childImageSharp {
fluid(maxWidth: 800) {
...GatsbyImageSharpFluid
}
}
}
}
html
fields {
slug
}
}
}
}
}
`)
return (
<section className="posts">
{data.allMarkdownRemark.edges.map((edge) => {
const id = edge.node.frontmatter.id;
const toggle = toggles[id];
const toggler = createToggler(id);
return (
<div className="post" id={edge.node.frontmatter.id}>
<div className="postDescrip">
<h2 className="postTitle">{edge.node.frontmatter.title}</h2>
<h2 className="name">{edge.node.frontmatter.name}</h2>
<button className="readMoreBtn" onClick={toggler}>{toggle ? <h2 className="readMore">Read more</h2> : <h2 className="readMore">Read less</h2>}
</button>
</div>
<Img className="postImg" fluid={edge.node.frontmatter.featuredImage.childImageSharp.fluid} />
<div className={toggle ? 'hide' : 'postCopy'} id={edge.node.frontmatter.id}>
<Close close={toggler} />
<h3>{edge.node.frontmatter.details}</h3>
<div className="copy" dangerouslySetInnerHTML= {{__html: edge.node.html}}></div>
<h4>Read the full article in Issue One</h4>
</div>
</div>
)}
)}
</section>
)
}
export default Posts;
[1]: https://i.stack.imgur.com/VhIYF.png
like this.
use a object instead of a single boolean.
const [toggles, setToggles] = useState ({});
const createToggler = (id) = () => {
setToggle(prev => { [id]: !prev[id] }) // atmost one id is true. others is undefine or false.
// setToggle(prev => { ...prev, [id]: !prev[id] }) // or support multi modal at same time. but I think you don't want it.
}
...
return (
<section className="posts">
{data.allMarkdownRemark.edges.map((edge) => {
const id = ... // get your id form edge.
const toggle = toggles[id];
const toggler = createToggler(id);
return (
<div className="post">
<div className="postDescrip">
<h2 className="postTitle">{edge.node.frontmatter.title}</h2>
<h2 className="name">{edge.node.frontmatter.name}</h2>
<button className="readMoreBtn" onClick={toggler}>{toggle ? <h2 className="readMore">Read more</h2> : <h2 className="readMore">Read less</h2>}
</button>
</div>
<Img className="postImg" fluid={edge.node.frontmatter.featuredImage.childImageSharp.fluid} />
<div className={toggle ? 'hide' : 'postCopy'} >
<Close close={toggler} />
<h3>{edge.node.frontmatter.details}</h3>
<div className="copy" dangerouslySetInnerHTML= {{__html: edge.node.html}}></div>
<h4>Read the full article in Issue One</h4>
</div>
</div>
)}
)}
</section>
)
}
export default Posts;
I solved my problem like this
import React, {useState} from "react"
import Img from "gatsby-image"
import './posts.css';
import cancel from '../images/cancel.png'
const Post = ({title, name, id, image, details, html}) => {
const [toggle, setToggle] = useState (true);
const toggler = () => {
setToggle(prev => !prev)
}
const selectPost= (event) =>{
let id = event.target.id,
postCopy = document.getElementById('hide' + id);
toggler.call(postCopy);
}
return (
<div className="post">
<div className="postDescrip">
<h2 className="postTitle">{title}</h2>
<h2 className="name">{name}</h2>
<button className="readMoreBtn" onClick={selectPost}>{toggle ? <h2 id={id} className="readMore">Read more</h2> : <h2 id={id} className="readMore">Read less</h2>}
</button>
</div>
<Img className="postImg" fluid={image} />
<div id={'hide' + id} className={toggle ? 'hide' : 'postCopy'} >
<button aria-label="Close" onClick={selectPost} className="closeBtn">
<img alt="close-button" src={cancel}/>
</button>
<h3>{details}</h3>
<div className="copy" dangerouslySetInnerHTML= {html}></div>
<h4>Read the full article in Issue One</h4>
</div>
</div>
)
}
export default Post;

Resources