[React/NextJS] Any React Devs that can help figure out where we've gone short with this Cart?

https://github.com/callum-laing/shopping-site/tree/main We're using localStorage and useEffect.. I think we're close, but we can't quite figure out the final hurdle to get over the finish line... We're working in app/cart/page.jsx and app/shop/page.jsx.
78 Replies
CDL
CDLOP9mo ago
pushes this to the top
b1mind
b1mind9mo ago
const Cart = () => {
const searchParams = useSearchParams();
const id = searchParams.id;
const storedItems = JSON.parse(localStorage.getItem("items"));

const [items, setItems] = useState(storedItems);

useEffect(() => {
if (id) {
const newItem = posts.filter((item) => item.id === id);
items.push(newItem)
setItems(items);
}
localStorage.setItem("items", JSON.stringify(items));
}, [items]);

return (
<div>
<h2>Please proceed to give me your card details!</h2>
<p>
{items.map((item) => (
<div key={item.id}>
{item.id}
{item.name}
{item.price}
{item.image}
</div>
))}
</p>
</div>
);
};

export default Cart;
const Cart = () => {
const searchParams = useSearchParams();
const id = searchParams.id;
const storedItems = JSON.parse(localStorage.getItem("items"));

const [items, setItems] = useState(storedItems);

useEffect(() => {
if (id) {
const newItem = posts.filter((item) => item.id === id);
items.push(newItem)
setItems(items);
}
localStorage.setItem("items", JSON.stringify(items));
}, [items]);

return (
<div>
<h2>Please proceed to give me your card details!</h2>
<p>
{items.map((item) => (
<div key={item.id}>
{item.id}
{item.name}
{item.price}
{item.image}
</div>
))}
</p>
</div>
);
};

export default Cart;
Something like this 🤔 In my mind this works 😂 I should find a playground to just put your code in mmm yea idk all sorts of issues when I try it in stack blitz use client seems like bs 🤣 casue I had to check to see if window exists to use localStorage which is fine but I thought that is what the whole use client string was for. cause its trying to run ont he server first. https://stackblitz.com/edit/stackblitz-starters-rtgg9j?description=The%20React%20framework%20for%20production&file=app%2Fcart%2Fpage.jsx,app%2Fshop%2Fpage.jsx&title=Next.js%20Starter idk if that works cause I didn't save it but yea... what a mess. Really don't get whats so hard about this... fukin React/NextJS
CDL
CDLOP9mo ago
you're not gonna give up are ya
b1mind
b1mind9mo ago
export default function Cart() {
const searchParams = useSearchParams();
const id = searchParams.id;

const store = localStorage.getItem('items');
const storedItems = JSON.parse(store);
const [items, setItems] = useState(storedItems);

useEffect(() => {
const newItem = posts.filter((item) => item.id === id);
if (newItem) {
setItems(newItem);
}
localStorage.setItem('items', JSON.stringify(items));
}, []);

return (
<div>
<h2>Please proceed to give me your card details!</h2>
<p>
{/* {items.map((item) => (
<div key={item.id}>
{item.id}
{item.name}
{item.price}
{item.image}
</div>
))} */}
</p>
</div>
);
}
export default function Cart() {
const searchParams = useSearchParams();
const id = searchParams.id;

const store = localStorage.getItem('items');
const storedItems = JSON.parse(store);
const [items, setItems] = useState(storedItems);

useEffect(() => {
const newItem = posts.filter((item) => item.id === id);
if (newItem) {
setItems(newItem);
}
localStorage.setItem('items', JSON.stringify(items));
}, []);

return (
<div>
<h2>Please proceed to give me your card details!</h2>
<p>
{/* {items.map((item) => (
<div key={item.id}>
{item.id}
{item.name}
{item.price}
{item.image}
</div>
))} */}
</p>
</div>
);
}
I mean I'm close 🤣 if it would just read the localStorage >.>;;
b1mind
b1mind9mo ago
No description
b1mind
b1mind9mo ago
cause it thinks its on the server and window object ofc does not exists
CDL
CDLOP9mo ago
should I build this in vite to use context api to finish it, then leave our attempt how it is until we find a fix? lol
b1mind
b1mind9mo ago
I mean you do you xD lol At this point I'm just back to remembering why I think its al lshit SvelteKit ftw
CDL
CDLOP9mo ago
hahaha yea, dont tempt me
b1mind
b1mind9mo ago
😂
CDL
CDLOP9mo ago
IF I enjoy BE, you know i'm jumping back on svelte quicker than you can say boo lol
b1mind
b1mind9mo ago
mmm I do see errors in my code but nothing helps if I can't get localStorage read... whats f'd too is I had it kinda working 😂
CDL
CDLOP9mo ago
so localstorage is the issue huh. best we get rid of it 😄 what about uhh... context? 🤣
b1mind
b1mind9mo ago
not going to solve your issues here its not localStorage persay its where the code runs as I just said. You would still want to use localStorage with Context API if you wanted a persitant store. So unless you were doing a SPA wouldn't matter here
CDL
CDLOP9mo ago
yea I was joking
b1mind
b1mind9mo ago
oh right just use a wrapper cause its boilplatey af xD
b1mind
b1mind9mo ago
Medium
useLocalStorage hook for Next.Js, typed, and SSR friendly
I am learning React and Next.js, and I needed a way to access the localStorage without running into problems with SSR. Hooks to the rescue.
CDL
CDLOP9mo ago
tryna wrap my head around context with their wordy wordyness in this course, quite the brain frazzler, but I get the jist of it.
b1mind
b1mind9mo ago
useLocalStorage
Custom hook that uses local storage to persist state across page reloads.
b1mind
b1mind9mo ago
cause in React you can't just do things without importing a wrapper fml 🤦‍♂️ I mean I have to check for browser in my Svelte Store too but >.>;;
import { writable } from 'svelte/store'
import { browser } from '$app/environment'

export const persistStore = function (key, initial) {
// if (browser && !document.hasStorageAccess()) document.requestStorageAccess()

const persist = browser ? localStorage.getItem(key) : false
const data = persist ? JSON.parse(persist) : initial

const store = writable(data, () => {
const unsubscribe = store.subscribe((value) => {
browser ? localStorage.setItem(key, JSON.stringify(value)) : false
})
return unsubscribe
})
return store
}
import { writable } from 'svelte/store'
import { browser } from '$app/environment'

export const persistStore = function (key, initial) {
// if (browser && !document.hasStorageAccess()) document.requestStorageAccess()

const persist = browser ? localStorage.getItem(key) : false
const data = persist ? JSON.parse(persist) : initial

const store = writable(data, () => {
const unsubscribe = store.subscribe((value) => {
browser ? localStorage.setItem(key, JSON.stringify(value)) : false
})
return unsubscribe
})
return store
}
This is what you would do with ContextAPI too probabaly 10x more code though 🤣
CDL
CDLOP9mo ago
just a bunch of words to me
b1mind
b1mind9mo ago
Yea I'm done I'll let someone who actually enjoys NextJS to help you I tried xD
CDL
CDLOP9mo ago
you gave it your best shot
b1mind
b1mind9mo ago
meh I gave it my best "I hate this" shot 🤣 the answers are there for ya just gotta implement it with the hook wrapper or making your own. almost be easier to just setup sqlite and use the RSC 😂
CDL
CDLOP9mo ago
I began doing it in vite just to see how it works wit hcontext etc to have a finished product until we can get someone to help, and I already regret starting lmao well that was the plan wasn't it, lol learn some backend, come back, jazz it up
b1mind
b1mind9mo ago
yea do that
CDL
CDLOP9mo ago
ill get rid of this vite project, we'll come back to this later if no-one helps in the mean time
CDL
CDLOP9mo ago
Hah.
No description
b1mind
b1mind9mo ago
😉 I use it all ALL THE THINGS Localstorage is great for local info
WhoSalty
WhoSalty9mo ago
"use client";
import posts from "../data";
import { useSearchParams } from "next/navigation";
import React, { useState, useEffect } from "react";

const Cart = () => {
const searchParams = useSearchParams();
const id = searchParams.get("id");

const [items, setItems] = useState(() => JSON.parse(localStorage.getItem("items") || "[]"));

useEffect(() => {
const newItem = posts.find((item) => item.id === parseInt(id));

if (newItem) {
return () => {
setItems((prevItems) => [...prevItems, newItem]);
localStorage.setItem("items", JSON.stringify([...items, newItem]));
}
}
}, []);

return (
<div>
<h2>Please proceed to give me your card details!</h2>
{items.map((item, index) => (
<div key={index}>
{item.id}
{item.name}
{item.price}
{item.image}
</div>
))}
</div>
);
};

export default Cart;
"use client";
import posts from "../data";
import { useSearchParams } from "next/navigation";
import React, { useState, useEffect } from "react";

const Cart = () => {
const searchParams = useSearchParams();
const id = searchParams.get("id");

const [items, setItems] = useState(() => JSON.parse(localStorage.getItem("items") || "[]"));

useEffect(() => {
const newItem = posts.find((item) => item.id === parseInt(id));

if (newItem) {
return () => {
setItems((prevItems) => [...prevItems, newItem]);
localStorage.setItem("items", JSON.stringify([...items, newItem]));
}
}
}, []);

return (
<div>
<h2>Please proceed to give me your card details!</h2>
{items.map((item, index) => (
<div key={index}>
{item.id}
{item.name}
{item.price}
{item.image}
</div>
))}
</div>
);
};

export default Cart;
try this
CDL
CDLOP9mo ago
I’ll try this when I’m home in a few hours, thanks for the attempt! 🤞 Alright I tried it, but it didn't fix the problem... nice effort though 😅
b1mind
b1mind9mo ago
what does it get hung on? I wasn't aware you could do anons like that in the useEffect/state hooks it looked like a interesting solution
CDL
CDLOP9mo ago
this appeared in the terminal, but in dev tools there's no error
No description
CDL
CDLOP9mo ago
but I get this
No description
b1mind
b1mind9mo ago
ah same issue
CDL
CDLOP9mo ago
aye
b1mind
b1mind9mo ago
ya its trying to use localStorage on the server which is like window what? use client is a lie? lol
glutonium
glutonium9mo ago
items.push() this feels illegal 😂
b1mind
b1mind9mo ago
I mean lol
glutonium
glutonium9mo ago
can't we do
setItem(preItem => preItem.push())
setItem(preItem => preItem.push())
b1mind
b1mind9mo ago
its faster than [newItem, ...items] but I don't konw how that works with React and rerendering if it ignores it.
glutonium
glutonium9mo ago
hmm fair i mean since u r not allowed to directly change state it just feels so wrong to do 😂
b1mind
b1mind9mo ago
the issue is getting localStorage read to not wig out thinking its serverside xD
glutonium
glutonium9mo ago
hmm i see damn i need to get good with react ;-;
b1mind
b1mind9mo ago
I think its horrid, but I don't have to use it lol
glutonium
glutonium9mo ago
i learnt about useState and useEffect and just ran away with my life
WhoSalty
WhoSalty9mo ago
Oh, it works in dev mode lol, got to do some extra things to make it run in production due to window and local storage
CDL
CDLOP9mo ago
yeah we figured that, nice attempt though! I think ill probably come back to it once ive learned soem backend and throw that in there instead haha
WhoSalty
WhoSalty9mo ago
ill give it another crack in a few, it was a fun challenge, anything to get me off css lol
"use client";
import posts from "../data";
import { useSearchParams } from "next/navigation";
import React, { useState, useEffect } from "react";
import { Suspense } from "react";

const CartComponent = () => {
const searchParams = useSearchParams();
const id = searchParams.get("id");

const [items, setItems] = useState(() => {
if (typeof window !== "undefined") {
return JSON.parse(localStorage.getItem("items") || "[]");
} else {
return [];
}
});

useEffect(() => {
const newItem = posts.find((item) => item.id === parseInt(id));

if (newItem) {
setItems((prevItems) => [...prevItems, newItem]);
localStorage.setItem("items", JSON.stringify([...items, newItem]));
}
}, []);

return (
<div>
<h2>Please proceed to give me your card details!</h2>
{items.map((item, index) => (
<div key={index}>
{item.id}
{item.name}
{item.price}
{item.image}
</div>
))}
</div>
);
};


export default function Cart() {
return (
<Suspense>
<CartComponent />
</Suspense>
)
}
"use client";
import posts from "../data";
import { useSearchParams } from "next/navigation";
import React, { useState, useEffect } from "react";
import { Suspense } from "react";

const CartComponent = () => {
const searchParams = useSearchParams();
const id = searchParams.get("id");

const [items, setItems] = useState(() => {
if (typeof window !== "undefined") {
return JSON.parse(localStorage.getItem("items") || "[]");
} else {
return [];
}
});

useEffect(() => {
const newItem = posts.find((item) => item.id === parseInt(id));

if (newItem) {
setItems((prevItems) => [...prevItems, newItem]);
localStorage.setItem("items", JSON.stringify([...items, newItem]));
}
}, []);

return (
<div>
<h2>Please proceed to give me your card details!</h2>
{items.map((item, index) => (
<div key={index}>
{item.id}
{item.name}
{item.price}
{item.image}
</div>
))}
</div>
);
};


export default function Cart() {
return (
<Suspense>
<CartComponent />
</Suspense>
)
}
Tenkes
Tenkes9mo ago
So the way Next works is it renders components on server and sends rendered HTML to the client. However sometimes we need to use client-side stuff in our components such as localStorage, React hooks etc. In that case we mark component as client component with "use client" at the top of the file so Next knows it should be rendered on the client . However client components are initially rendered on server, and then on client. I'm not sure exactly how and why that works but that's why sometimes we need to first make sure we're on client-side using if (typeof window !== 'undefined') {/* ... */} before doing some client side stuff. We don't need it for hooks though, I guess they have client-side check inside of them or something lol. @b1mind hope this clears some things out 😂
b1mind
b1mind9mo ago
I already understood this I'm triggered by how stupid it is to have use-client and still have to check for window But I get it cause if it can it will render it out ssr first then client SvelteKit is very much the same way and* you do have to check if browser there too. >.>;;
We don't need it for hooks though, I guess they have client-side check inside of them or something lol.
can you give an example of this cause yea that is what I thought if it was inside a effect/state hook you didn't need to check window object. This seems to work 🤘 well kinda... seems to keep the last added in the array so you get two of it till you come back 🤔
WhoSalty
WhoSalty9mo ago
have you cleared localstorage
b1mind
b1mind9mo ago
yea mate
b1mind
b1mind9mo ago
No description
WhoSalty
WhoSalty9mo ago
i didn’t seem to get that 🤔 but code is code
b1mind
b1mind9mo ago
No description
b1mind
b1mind9mo ago
facts xD lol
WhoSalty
WhoSalty9mo ago
im unhappy with it anyway due to the exhaustive deps warning 😢
b1mind
b1mind9mo ago
useEffect(() => {
const newItem = posts.find((item) => item.id === parseInt(id));

if (newItem) {
setItems([...items, newItem]);
localStorage.setItem('items', JSON.stringify([...items, newItem]));
}
}, []);
useEffect(() => {
const newItem = posts.find((item) => item.id === parseInt(id));

if (newItem) {
setItems([...items, newItem]);
localStorage.setItem('items', JSON.stringify([...items, newItem]));
}
}, []);
seems to do it
WhoSalty
WhoSalty9mo ago
im going to try and move the deps into the useeffect but when i try i get loops lol and still end up needing searchParams
b1mind
b1mind9mo ago
you could use a dynamic route too as I was telling CDL
WhoSalty
WhoSalty9mo ago
i’m honestly not familiar with nextjs new apps route thing, still doing it the old way
b1mind
b1mind9mo ago
old way the clientside only way? or pre folder routing you mean?
WhoSalty
WhoSalty9mo ago
uhm, just the structure, src/api, src/pages etc the latter i suppose
b1mind
b1mind9mo ago
gotcha yea it is teh way I don't do React though so I don't have a leg in the race 🤣 if I did I'd probably pick Remix though
WhoSalty
WhoSalty9mo ago
i think i ran into problems utilizing Prisma Orm with the new way ran back to what i knew anyways, i relearned a lot about useEffect trying to fix this, 🫡
b1mind
b1mind9mo ago
Yea I gotta learn a little bit too, and how much I'd rather still use SvelteKit 🤣
CDL
CDLOP9mo ago
glad you did useEffect, I hate it. Hah I get the item being added twice with your latest attempt, update: it seems to work
b1mind
b1mind9mo ago
if you use the edited bit I did you wont this bit
WhoSalty
WhoSalty9mo ago
I am still thinking about if the localstorage set should be done on unmount or not 🤔
b1mind
b1mind9mo ago
I mean it should be a persistent store but this works for now 😄
WhoSalty
WhoSalty9mo ago
yeah if it works :thumbup:
b1mind
b1mind9mo ago
this is what I'd probably use, or make my own.
CDL
CDLOP9mo ago
yeah don't need to go wild on it haha, I really appreciate the efforts!
WhoSalty
WhoSalty9mo ago
how do we make it work without exhaustive deps warning 😩 you would think an empty dep array is a react feature so that we can explicitly state we want this effect to run once
CDL
CDLOP9mo ago
sorry what deps warning? I seem to get this
No description
CDL
CDLOP9mo ago
though weirdly, now whwnever i refresh it adds an item to the cart
WhoSalty
WhoSalty9mo ago
i get an exhaustive deps lint warning when building no me gusta eso amigo 👴🏼
Want results from more Discord servers?
Add your server