I've been troubleshooting and I narrowed it down to the useEffect.
This code as-is won't fire the dispatch(getOrderByIdAction(id)) as shown in the redux dev tools.
import { useDispatch, useSelector } from "react-redux";
import { useEffect } from "react";
import { useParams, Link } from "react-router-dom";
import CheckoutSteps from '../components/CheckoutSteps'
import { getOrderByIdAction } from '../state/actions/orderActions'
import Message from '../components/Message'
const OrderConfirmationScreen = () => {
const dispatch = useDispatch()
const { id } = useParams()
// From Order Confirmation State
const orderDetails = useSelector((state) => state.orderConfirmation.orderDetails)
const shippingAddress = orderDetails.shippingAddress
const paymentMethod = orderDetails.paymentMethod
const orderItems = orderDetails.orderItems
// Calculator Order Summary
const itemPrice = orderItems.reduce((acc, cur) => {
return acc + (cur.price * cur.qty)
}, 0)
const taxPrice = Number((itemPrice * .1).toFixed(2))
const totalPrice = taxPrice + itemPrice
useEffect(() => {
if (id) {
dispatch(getOrderByIdAction(id))
}
},[])
return (
<div className="main-container lg:p-16">
<CheckoutSteps step1 step2 step3 />
<div className="max-w-6xl mx-auto p-8 my-8 w-full">
<div className="flex flex-col lg:flex-row">
<div className="place-order-container bg-gray-50 rounded-md shadow-2xl m-2 p-6 lg:p-12 w-full">
<h1 className="text-center font-semibold text-xl mt-4 mb-6">Place Order</h1>
{/* { error && <Message message={ "Hello" }/>} */}
<div className="order-header-group grid grid-cols-2 bg-white rounded-lg shadow-xl pt-4 pb-8 px-2">
<div className="order-shipping-section my-2 mx-4 lg:p-2">
<h4 className="font-medium my-4">Shipping Address: </h4>
<div className="address-group">
<h6 className="text-sm">{shippingAddress.address}</h6>
<h6 className="text-sm">{shippingAddress.city}, {shippingAddress.zipcode}</h6>
<h6 className="text-sm">{shippingAddress.phone}</h6>
{ shippingAddress &&
<Link to={'/shipping'} className="underline my-2 block">Edit</Link> }
</div>
</div>
<div className="order-payment-section my-2 lg:p-2">
<h4 className="font-medium my-4">Payment Method: </h4>
{ paymentMethod === 'Paypal' && <i className="fab fa-paypal fa-lg mr-2" style={{color: "#253B80"}}></i>}
<span className="text-sm">{paymentMethod}</span>
</div>
</div>
<div className="order-items-group my-3 bg-white rounded-lg shadow-2xl p-2 lg:p-4">
<div className="order-items-group rounded-md p-6">
{ orderItems.map((item, index) =>
<div key={item.productId} className="grid grid-cols-8 gap-2 items-center my-4">
<img src={item.image} alt={item.name} className="col-span-2 w-40 p-4" />
<h6 className="col-span-3 text-sm">{item.name}</h6>
<h6 className="col-span-1 text-sm text-center">{item.qty}</h6>
<h6 className="col-span-1 text-sm text-center">x</h6>
<h6 className="col-span-1 text-sm text-center">{item.price}</h6>
<hr className="my-4 col-span-8"/>
</div>
)}
</div>
</div>
</div>
<div className="order-total-section bg-gray-50 rounded-md shadow-2xl m-2 p-6 lg:pb-8 w-full lg:w-2/5 h-1/2">
<h4 className="font-semibold text-xl text-center my-8">Order Summary</h4>
<div className="bg-white rounded-lg shadow-2xl p-4">
<div className="items-total grid grid-cols-3 p-1">
<span className="col-span-2 my-2 block text-sm">Items: </span>
<span className="col-span-1 my-2 block text-sm">${itemPrice} </span>
</div>
<div className="shipping-total grid grid-cols-3 p-1">
<span className="col-span-2 my-2 block text-sm">Shipping: </span>
<span className="col-span-1 my-2 block text-sm">FREE </span>
</div>
<div className="tax-total grid grid-cols-3 p-1">
<span className="col-span-2 my-2 block text-sm">Tax: </span>
<span className="col-span-1 my-2 block text-sm">${taxPrice} </span>
</div>
<hr className="my-2" />
<div className="total-total grid grid-cols-3 p-1">
<span className="col-span-2 my-2 block text-sm font-semibold">Total: </span>
<span className="col-span-1 my-2 block text-sm font-semibold">${totalPrice} </span>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default OrderConfirmationScreen;
If I just run dispatch without useEffect, it will fire off with a success and I get all the order info on my reducer. So I know it has something to do with the useEffect.
Unfortunately, then I get an error that says orderDetails is undefined because I need useEffect to run it before the component renders?
Not sure what I did wrong
EDIT:
instructor's orderScreen.js
const OrderScreen = ({ match, history }) => {
const orderId = match.params.id
const [sdkReady, setSdkReady] = useState(false)
const dispatch = useDispatch()
const orderDetails = useSelector((state) => state.orderDetails)
const { order, loading, error } = orderDetails
const orderPay = useSelector((state) => state.orderPay)
const { loading: loadingPay, success: successPay } = orderPay
const orderDeliver = useSelector((state) => state.orderDeliver)
const { loading: loadingDeliver, success: successDeliver } = orderDeliver
const userLogin = useSelector((state) => state.userLogin)
const { userInfo } = userLogin
if (!loading) {
// Calculate prices
const addDecimals = (num) => {
return (Math.round(num * 100) / 100).toFixed(2)
}
order.itemsPrice = addDecimals(
order.orderItems.reduce((acc, item) => acc + item.price * item.qty, 0)
)
}
useEffect(() => {
if (!userInfo) {
history.push('/login')
}
const addPayPalScript = async () => {
const { data: clientId } = await axios.get('/api/config/paypal')
const script = document.createElement('script')
script.type = 'text/javascript'
script.src = `https://www.paypal.com/sdk/js?client-id=${clientId}`
script.async = true
script.onload = () => {
setSdkReady(true)
}
document.body.appendChild(script)
}
if (!order || successPay || successDeliver || order._id !== orderId) {
dispatch({ type: ORDER_PAY_RESET })
dispatch({ type: ORDER_DELIVER_RESET })
dispatch(getOrderDetails(orderId))
} else if (!order.isPaid) {
if (!window.paypal) {
addPayPalScript()
} else {
setSdkReady(true)
}
}
}, [dispatch, orderId, successPay, successDeliver, order])
const successPaymentHandler = (paymentResult) => {
console.log(paymentResult)
dispatch(payOrder(orderId, paymentResult))
}
const deliverHandler = () => {
dispatch(deliverOrder(order))
}
return loading ? (
<Loader />
) : error ? (
<Message variant='danger'>{error}</Message>
) : ( jsx
edited code
OrderConfirmation.js
const OrderConfirmationScreen = () => {
const dispatch = useDispatch()
const { id } = useParams()
// From Order Confirmation State
const orderConfirmation = useSelector((state) => state.orderConfirmation)
const { loading, error, orderDetails } = orderConfirmation
useEffect(() => {
if(id) {
console.log(id);
dispatch(getOrderByIdAction(id))}
},[id])
const shippingAddress = orderDetails.shippingAddress
const paymentMethod = orderDetails.paymentMethod
const orderItems = orderDetails.orderItems
// Calculator Order Summary
const itemPrice = orderItems.reduce((acc, cur) => {
return acc + (cur.price * cur.qty)
}, 0)
const taxPrice = Number((itemPrice * .1).toFixed(2))
const totalPrice = taxPrice + itemPrice
return loading? (<h3>loading..</h3>) :
( jsx
Since the data is not in the store when you render the component, you have to wait for the http request to complete.
You can return null when orderDetails is undefined, but consider adding a loading and error state to give a better user experience and make it easier for you to render a loading component or displaying an error
const OrderConfirmationScreen = () => {
const dispatch = useDispatch();
const { id } = useParams();
// From Order Confirmation State
const orderDetails = useSelector(
state => state.orderConfirmation.orderDetails,
);
useEffect(() => {
if (id) {
dispatch(getOrderByIdAction(id));
}
}, [id]);
// This is the important part.
// consider replacing this condition with loading and error from your state
if (!orderDetails) return <h3>Loading ...</h3>;
const { shippingAddress, paymentMethod, orderItems } = orderDetails;
// Calculator Order Summary
const itemPrice = orderItems.reduce((acc, cur) => {
return acc + cur.price * cur.qty;
}, 0);
const taxPrice = Number((itemPrice * 0.1).toFixed(2));
const totalPrice = taxPrice + itemPrice;
return jsx...
};
Related
currently creating a project with the REST Countries API and it's my first time using Router, having a bit of an issue with an API call that is supposed to grab a single country when clicked. For quick reference I uploaded a sandbox here
Routes are set up in App like this:
function App() {
return (
<main>
<Routes>
<Route path="/" element={<CountriesHome />} />
<Route path="/countrydetail/:countryName" element={<CountryDetail />} />
</Routes>
</main>
);
}
CountriesHome.js then has an onClick on each country in the homepage that uses navigate:
<section
className="flex flex-col cursor-pointer"
key={index}
onClick={() => navigate(`/countrydetail/${cca2.toLowerCase()}`, {})
}
>
And inside CountryDetail, I'm trying to useParams to call a specific country, in this case the one clicked by the user, from the API
let { countryName } = useParams();
const [country, setCountry] = useState();
const getCountry = async () => {
try {
const response = await fetch(
`https://restcountries.com/v3.1/alpha/${countryName}`
);
const data = await response.json();
setCountry(data);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
getCountry();
}, [countryName]);
However, I'm just not getting the results I'm expecting with this one, country is returning as undefined and I cannot destructure and render its values.
Because country is an array, so I changed your code like:
const CountryDetail = () => {
let navigate = useNavigate();
let { countryName } = useParams();
const [country, setCountry] = useState([]);
const getCountry = async () => {
try {
const response = await fetch(
`https://restcountries.com/v3.1/alpha/${countryName}`
);
const data = await response.json();
if (data) {
setCountry(data);
}
} catch (error) {
console.log(error);
} finally {
}
};
useEffect(() => {
getCountry();
}, [countryName]);
return (
<div className="w-11/12 mx-auto grid justify-items-center grid-cols-1 md:grid-cols-3 md:gap-10 lg:grid-cols-4">
<div className="w-80 h-56 mb-4 bg-slate-900"></div>
{country?.map((val) => (
<div>
<section className="justify-self-start pl-4 mb-6">
<h1 className="font-extrabold text-lg mb-4 ">
{val?.name?.common}
</h1>
<h5>
<span className="font-semibold">Native Name: </span>
{val?.name?.official}
</h5>
<h5>
<span className="font-semibold">Population: </span>
{val?.population}
</h5>
<h5>
<span className="font-semibold">Region: </span>
{val?.region}
</h5>
<h5>
<span className="font-semibold">Sub Region: </span>
{val?.subregion}
</h5>
<h5>
<span className="font-semibold">Capital: </span>
{val?.capital}
</h5>
</section>
<section className="justify-self-start pl-4">
<h5>
<span className="font-semibold">Top Level Domain: </span>
{val?.tld}
</h5>
<h5>
<span className="font-semibold">Currencies: </span>
{val?.currencies &&
Object.values(val?.currencies).map((currency) => {
return <span>{currency.name}, </span>;
})}
</h5>
<h5>
<span className="font-semibold">Languages: </span>
{val?.languages &&
Object.entries(val?.languages).map(([key, value]) => {
return <span className="m-1">{value}</span>;
})}
</h5>
</section>
<section className="justify-self-start pl-4 mb-6 ">
<h3 className="font-semibold text-lg">Border Countries: </h3>
{val?.borders &&
val?.borders.map((country) => {
return (
<button className="w-28 py-2 m-2 shadow-[0px_0px_4px_1px_rgba(0, 0, 0, 0.104931)] border-solid border-2 rounded-sm">
{country}
</button>
);
})}
</section>
</div>
))}
</div>
);
};
//
export default CountryDetail;
with react DND only one element accepts the drag reference i fetch data with firebase and put them in an array then map through the items and generate a card but only the last element accepts the drag ref
this is the place where I'm trying to implement it
import { useDrag } from "react-dnd";
const db = getFirestore(app);
export default function Projects({ currentUid }) {
const [projects, setProjects] = useState([]);
const [doneProjects, setDoneProjects] = useState([]);
const [tasks, setTasks] = useState([]);
const [dropDown, setDropDown] = useState(false);
const [key, setKey] = useState();
const [documentId, setDocumentId] = useState();
const [progressPercentage, setProgressPercentage] = useState(0);
const [{ isDragging }, dragRef] = useDrag(
() => ({
type: itemTypes.CARD,
item: { type: itemTypes.CARD },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
})
}),
[]
)
useEffect(() => {
// amay xwarawa bo aw projecta naya ka tawaw bwn
// const docRef3 = query(collection(db, "assignedProjects", currentUid, "projects"),where("isDone", "==", true));
const getProjects = async () => {
const docRef3 = query(
collection(db, "assignedProjects", currentUid, "projects"),
where("isDone", "==", true)
);
const docDoneSnap = await getDocs(docRef3);
onSnapshot(docRef3, (docDoneSnap) => {
const projectsDoneArr = [];
docDoneSnap.forEach((doc) => {
projectsDoneArr.push(doc.data());
});
setDoneProjects(projectsDoneArr);
});
const docRef = query(
collection(db, "assignedProjects", currentUid, "projects"),
where("isDone", "==", false)
);
// const docSnap = await getDocs(docRef);
// const projectsArr = [];
// docSnap.forEach((doc) => {
// projectsArr.push(
// {
// projects:doc.data(),
// projectId:doc.id
// }
// );
// documentId = doc.id;
// // console.log(doc.data());
// });
onSnapshot(docRef, (docSnap) => {
const projectsArr = [];
docSnap.forEach((doc) => {
projectsArr.push(
{
projects:doc.data(),
projectId:doc.id
}
);
setDocumentId(doc.id);
// console.log(doc.data());
});
setProjects(projectsArr);
});
console.log(documentId)
const taskRef = query(
collection(db, "assignedProjects", currentUid, "tasks"),
where("projectId", "==", documentId)
);
const taskSnap = await getDocs(taskRef);
const tasksArr = taskSnap.docs.map((doc) => doc.data());
setTasks(tasksArr);
};
getProjects();
}, []);
console.log(tasks)
useEffect(() => {
const getProgressPercentage = () => {
if (projects.length === 0) {
setProgressPercentage(100);
return;
}
const totalProjects = projects.length;
const totalDoneProjects = doneProjects.length;
const totalTasks = tasks.length;
const totalDoneTasks = tasks.filter((task) => task.isDone === true)
.length;
const totalProgress = totalDoneProjects + totalDoneTasks;
const total = totalProjects + totalTasks;
const percentage = Math.round((totalProgress / total) * 100);
setProgressPercentage(percentage);
};
getProgressPercentage();
}, [projects, doneProjects, tasks]);
function markProjectDone(project) {
const projectRef = collection(
db,
"assignedProjects",
currentUid,
"projects"
);
const docRef = doc(projectRef, project.projectId);
updateDoc(docRef, {
isDone: true,
});
}
const tasksToShow=tasks.map((task) => (
<div className={""}>
<p className="h-fit px-10 my-2 py-2 bg-slate-200 dark:bg-[#252525] rounded-2xl text-xl text-start">
{task.task1}
</p>
<p className="h-fit px-10 my-2 py-2 bg-slate-200 dark:bg-[#252525] rounded-2xl text-xl text-start">
{task.task2}
</p>
<p className="h-fit px-10 my-2 py-2 bg-slate-200 dark:bg-[#252525] rounded-2xl text-xl text-start">
{task.task3}
</p>
</div>
))
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.75, ease: "easeOut" }}
className="w-full min-h-full items-center font-Main text-[#121212] dark:text-[#ffffff] flex justify-between"
>
<div className="flex flex-col gap-20 h-full justify-around">
{/* <Progress value={50} label="Completed" className="bg-cyan-500" /> */}
{/* {isNaN(progressPercentage)?"Loading ...":<div className='h-5 w-full bg-gray-300'>
<div
style={{ width: `${progressPercentage}%`}}
className={`h-full ${
progressPercentage < 70 ? 'bg-red-600' : 'bg-green-600'}`}>
{progressPercentage}%
</div>
</div>} */}
<h1 className="text-6xl">Projects in progress</h1>
{projects.map((project) => (
<div ref={dragRef} key={project.id} className=" text-[#121212] dark:text-[#ffffff] flex flex-col gap-2" draggable={true} >
<div className="bg-slate-200 dark:bg-[#252525] w-[35rem] items-center h-32 rounded-2xl py-2 flex justify-between px-10" >
<div className="flex flex-col items-start justify-start gap-2" >
<h1 className="text-4xl text-[#018786] dark:text-[#03dac6] ">
{project.projects.title}
</h1>
<p className="text-lg text-[#12121247] dark:text-[#ffffff70]">
Due date :{" "}
<span className="text-[#121212c5] dark:text-[#ffffffb5] ">
{project.projects.dueDate}
</span>
</p>
</div>
<div className="text-5xl">
<button
onClick={() => {
setKey(project.title);
setDropDown(dropDown === false ? true : false);
}}
className="mx-5 dark:text-[#bb86fc] text-[#6200ee]"
>
{dropDown && key === project.title ? (
<ion-icon name="chevron-up-circle-outline"></ion-icon>
) : (
<ion-icon name="chevron-down-circle-outline"></ion-icon>
)}
</button>
<button className="text-[#018786] dark:text-[#03dac6]" onClick={()=>{
markProjectDone(project)
}}>
<ion-icon name="checkmark-circle-outline"></ion-icon>
</button>
</div>
</div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.75, ease: "easeOut" }}
className={`h-fit transition-all ${
dropDown && key === project.title ? "" : "hidden"
} w-full py-2`}
>
<div
className={`h-fit px-10 py-2 dark:bg-[#3700b3] bg-[#6200ee] rounded-2xl w-full flex flex-col justify-start`}
>
<div className="h-fit w-full ">
<p className="text-xl flex text-[#ffffffc1]">
Description :{" "}
<span className=" text-[#ffffff] ">
<p>{project.projects.description}</p>
</span>
</p>
</div>
<p>{project.projects.status}</p>
</div>
{tasksToShow}
</motion.div>
</div>
))}
</div>
<div className="flex flex-col gap-20 h-full justify-around">
<h1 className="text-6xl">Done Projects</h1>
{doneProjects.map((project) => (
<div className="">
<div className=" bg-[#0187878e] dark:bg-[#03dac579] w-[35rem] items-center h-32 rounded-2xl py-2 flex justify-between px-10">
<div className="flex flex-col items-start justify-start gap-2">
<h1 className="text-4xl text-[#121212]">{project.title}</h1>
<p className="text-lg text-[#12121247] ">
Due date :{" "}
<span className="text-[#121212c5] ">{project.dueDate}</span>
</p>
</div>
</div>
</div>
))}
</div>
</motion.div>
);
}
and this is the index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
import {DndProvider} from "react-dnd";
import {HTML5Backend} from "react-dnd-html5-backend";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<BrowserRouter>
<DndProvider backend={HTML5Backend}>
<App />
</DndProvider>
</BrowserRouter>
</React.StrictMode>
);
I tried putting the reference in different divs but still the same.
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
When I compile this code, it gives me an error saying that I am not properly closing the a tag.
When I remove the conditional statement on the following code, it works fines:
{nid[index] != undefined ? (
<a target= "_blank" rel="noreferrer" href={polygonURL + Cont[index] + "/" + nid[index]}>
) : (
<a target= "_blank" rel="noreferrer" href={unkpolygonURL + Cont[index]}>
)}
import styles from "../styles/Home.module.css";
import React, { useEffect, useState, useRef } from "react";
import { useAddress, useMetamask, useDisconnect } from "#thirdweb-dev/react";
import Spinner from "./Spinner";
export default function NFT() {
const address = useAddress();
let image = "";
let total = 0;
const [title, setTitle] = useState([]);
const [Img, setImg] = useState([]);
const [Cont, setCont] = useState([]);
const [nid, setNid] = useState([]);
const[totalNFT, setTotalNFT] = useState(0);
let page = "";
const polygonURL = "https://opensea.io/assets/matic/";
const unkpolygonURL = "https://opensea.io/assets?search[query]=";
const getPolygonNFT = async (str) => {
// const api = 'https://eth-mainnet.alchemyapi.io/v2/demo/getNFTs/?owner=0xfae46f94ee7b2acb497cecaff6cff17f621c693d';
const api = `https://polygon-mainnet.g.alchemyapi.io/v2/demo/getNFTs/?owner=` + str;
const response = await fetch(api);
const data = await response.json();
console.log(data);
let total = data.totalCount;
let firsttotal = data.ownedNfts.length;
setTotalNFT(data.ownedNfts.length);
// setPage(data.pageKey);
let page = data.pageKey;
for (var i = 0; i < firsttotal; i++){
setImg(oldArray => [...oldArray, data.ownedNfts[i].media[0].gateway]);
setTitle(oldArray => [...oldArray, data.ownedNfts[i].metadata.name]);
setCont(oldArray => [...oldArray, data.ownedNfts[i].contract.address]);
setNid(oldArray => [...oldArray, data.ownedNfts[i].metadata.edition])
}
let j = true;
let count = 0;
while (j){
if (page != undefined){
const api = `https://polygon-mainnet.g.alchemyapi.io/v2/demo/getNFTs/?owner=` + str + `&pageKey=` + page;
const response = await fetch(api);
const data = await response.json();
console.log(data)
page = data.pageKey;
let nexttotal = data.ownedNfts.length;
for (var k = count; k < nexttotal+count ; k++){
setImg(oldArray => [...oldArray, data.ownedNfts[k].media[0].gateway]);
setTitle(oldArray => [...oldArray, data.ownedNfts[k].metadata.name]);
setCont(oldArray => [...oldArray, data.ownedNfts[k].contract.address]);
setNid(oldArray => [...oldArray, data.ownedNfts[k].metadata.edition])
}
} else {
j = false;
}
}
}
useEffect(() => {
getPolygonNFT(address);
}, []);
return (
<main className="inside p-10 shadow-xl shadow-gray-400/20">
{totalNFT === 0 ? (
<>
{totalNFT !== 0 ? (
<h1 className="mb-10 text-4xl font-extralight" >
<span className='text-slate-300 font-extrabold underline decoration-neutral-600'>You do not own any NFTs.</span>
</h1>
) : (
<Spinner />
)}
</>
) : (
<>
<div className="grid space-x-3 md:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4">
{title.map ( (name, index) => {
return (
<div name={index} key={index}>
{nid[index] != undefined ? (
<a target= "_blank" rel="noreferrer" href={polygonURL + Cont[index] + "/" + nid[index]}>
) : (
<a target= "_blank" rel="noreferrer" href={unkpolygonURL + Cont[index]}>
)}
<div className="flex flex-col items-center cursor-pointer transition-all duration-200 hover:scale-105">
<img className="h-80 w-80 rounded-2xl object-cover" src={Img[index]} alt=""
onError={({ currentTarget }) => {
currentTarget.onerror = null; // prevents looping
currentTarget.src="https://user-images.githubusercontent.com/24848110/33519396-7e56363c-d79d-11e7-969b-09782f5ccbab.png";
}}
/>
<div className="p-5">
<h2 className="text-gray-100 text-3xl">{title[index]}</h2>
<p className="mt-2 text-sm text-gray-400">{Cont[index]}</p>
</div>
</div>
</a>
</div>
)
})
}
</div>
</>
)
}
</main>
)}
import styles from "../styles/Home.module.css";
import React, { useEffect, useState, useRef } from "react";
import { useAddress, useMetamask, useDisconnect } from "#thirdweb-dev/react";
import Spinner from "./Spinner";
export default function NFT() {
const address = useAddress();
let image = "";
let total = 0;
const [title, setTitle] = useState([]);
const [Img, setImg] = useState([]);
const [Cont, setCont] = useState([]);
const [nid, setNid] = useState([]);
const [totalNFT, setTotalNFT] = useState(0);
let page = "";
const polygonURL = "https://opensea.io/assets/matic/";
const unkpolygonURL = "https://opensea.io/assets?search[query]=";
const getPolygonNFT = async (str) => {
// const api = 'https://eth-mainnet.alchemyapi.io/v2/demo/getNFTs/?owner=0xfae46f94ee7b2acb497cecaff6cff17f621c693d';
const api =
`https://polygon-mainnet.g.alchemyapi.io/v2/demo/getNFTs/?owner=` + str;
const response = await fetch(api);
const data = await response.json();
console.log(data);
let total = data.totalCount;
let firsttotal = data.ownedNfts.length;
setTotalNFT(data.ownedNfts.length);
// setPage(data.pageKey);
let page = data.pageKey;
for (var i = 0; i < firsttotal; i++) {
setImg((oldArray) => [...oldArray, data.ownedNfts[i].media[0].gateway]);
setTitle((oldArray) => [...oldArray, data.ownedNfts[i].metadata.name]);
setCont((oldArray) => [...oldArray, data.ownedNfts[i].contract.address]);
setNid((oldArray) => [...oldArray, data.ownedNfts[i].metadata.edition]);
}
let j = true;
let count = 0;
while (j) {
if (page != undefined) {
const api =
`https://polygon-mainnet.g.alchemyapi.io/v2/demo/getNFTs/?owner=` +
str +
`&pageKey=` +
page;
const response = await fetch(api);
const data = await response.json();
console.log(data);
page = data.pageKey;
let nexttotal = data.ownedNfts.length;
for (var k = count; k < nexttotal + count; k++) {
setImg((oldArray) => [
...oldArray,
data.ownedNfts[k].media[0].gateway,
]);
setTitle((oldArray) => [
...oldArray,
data.ownedNfts[k].metadata.name,
]);
setCont((oldArray) => [
...oldArray,
data.ownedNfts[k].contract.address,
]);
setNid((oldArray) => [
...oldArray,
data.ownedNfts[k].metadata.edition,
]);
}
} else {
j = false;
}
}
};
useEffect(() => {
getPolygonNFT(address);
}, []);
return (
<main className="inside p-10 shadow-xl shadow-gray-400/20">
{totalNFT === 0 ? (
<>
{totalNFT !== 0 ? (
<h1 className="mb-10 text-4xl font-extralight">
<span className="text-slate-300 font-extrabold underline decoration-neutral-600">
You do not own any NFTs.
</span>
</h1>
) : (
<Spinner />
)}
</>
) : (
<div className="grid space-x-3 md:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4">
{title.map((name, index) => {
return (
<div name={index} key={index}>
{nid[index] != undefined ? (
<a
target="_blank"
rel="noreferrer"
href={polygonURL + Cont[index] + "/" + nid[index]}
>
<div className="flex flex-col items-center cursor-pointer transition-all duration-200 hover:scale-105">
<img
className="h-80 w-80 rounded-2xl object-cover"
src={Img[index]}
alt=""
onError={({ currentTarget }) => {
currentTarget.onerror = null; // prevents looping
currentTarget.src =
"https://user-images.githubusercontent.com/24848110/33519396-7e56363c-d79d-11e7-969b-09782f5ccbab.png";
}}
/>
<div className="p-5">
<h2 className="text-gray-100 text-3xl">
{title[index]}
</h2>
<p className="mt-2 text-sm text-gray-400">
{Cont[index]}
</p>
</div>
</div>
</a>
) : (
<a
target="_blank"
rel="noreferrer"
href={unkpolygonURL + Cont[index]}
>
<div className="flex flex-col items-center cursor-pointer transition-all duration-200 hover:scale-105">
<img
className="h-80 w-80 rounded-2xl object-cover"
src={Img[index]}
alt=""
onError={({ currentTarget }) => {
currentTarget.onerror = null; // prevents looping
currentTarget.src =
"https://user-images.githubusercontent.com/24848110/33519396-7e56363c-d79d-11e7-969b-09782f5ccbab.png";
}}
/>
<div className="p-5">
<h2 className="text-gray-100 text-3xl">
{title[index]}
</h2>
<p className="mt-2 text-sm text-gray-400">
{Cont[index]}
</p>
</div>
</div>
</a>
)}
</div>
);
})}
</div>
)}
</main>
);
}
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
});