Next.js Discord

Discord Forum

Query params update but app doesn't (sometimes)

Answered
berkserbet posted this in #help-forum
Open in Discord
Avatar
berkserbetOP
I have an ecommerce app with some filters. When the filters are checked the url on my site updates. [my site](https://storefront.community/)

But sometimes when the url changes the products don't get filtered. So there basically isn't a state change. I can recreate it, so it's not random - but I don't understand what could be the issue. I'm new to nextjs - but haven't seen url update not update the app.
Answered by berkserbet
Thanks for the help!
View full answer

898 Replies

Avatar
DirtyCajunRice | AppDir
a link to your site isnt relevant. Please post code showing where you get the params, and where you consume them
Avatar
berkserbetOP
Sure, I just didn't want to post a ton of code. Thank you
Avatar
berkserbetOP
It's just one page.

Here is /app/page.tsx
import React, { Suspense } from 'react'
import ProductBrowser from './ProductBrowser';

interface Props {
  searchParams: {
    r: string,
    search: string,
    genders: string,
    sizes: string,
    conditions: string,
    countries: string,
    brands: string,
    pages: number
  }
}

export default function Home({searchParams: { r, search, genders, sizes, conditions, countries, brands, pages }}: Props) {

  return (
    <main>
      <Suspense fallback={<div className="h-screen flex items-center justify-center pb-56"><span className="loading loading-dots loading-lg text-secondary text-center h-screen flex items-center justify-center"></span></div>}>
        <ProductBrowser search={search} selected_subreddits={r} selected_genders={genders} selected_sizes={sizes} selected_conditions={conditions} selected_countries={countries} selected_brands={brands} pages={pages}/>
      </Suspense>
    </main>
  );
}


Here is /app/ProductBrowser.tsx
import React from 'react'
import { promises as fs } from 'fs';
import path from 'path'
import ProductCards from './ProductCards'


interface Props {
  search: string,
  selected_subreddits: string,
  selected_genders: string,
  selected_sizes: string,
  selected_conditions: string,
  selected_countries: string,
  selected_brands: string,
  pages: number
}

const ProductBrowser = async ({ search, selected_subreddits, selected_genders, selected_sizes, selected_conditions, selected_countries, selected_brands, pages }: Props) => {

  const filePath = path.join(process.cwd(), 'json/listings.json');
  const file = await fs.readFile(filePath, 'utf8');
  const products = JSON.parse(file)
  
  return (
    <ProductCards products={products} search={search} selected_subreddits={selected_subreddits} selected_genders={selected_genders} selected_sizes={selected_sizes} selected_conditions={selected_conditions} selected_countries={selected_countries} selected_brands={selected_brands} pages={pages}/>
  )
}

export default ProductBrowser
Avatar
DirtyCajunRice | AppDir
um
so…
Avatar
berkserbetOP
I was about to share the last file
Avatar
DirtyCajunRice | AppDir
first off
Avatar
berkserbetOP
Do you already see an issue?
Avatar
DirtyCajunRice | AppDir
there are some silly coding habits im gonna point out
so you can improve
Avatar
berkserbetOP
Please do! I'm new to this
Avatar
DirtyCajunRice | AppDir
the search params are the same as the props for the component
you are directly passing them
so define them in the component only
export the interface
and import it to page and use it
then you dont have to spread searchParams
you can do <ProductBrowser {...searchParams} />
Avatar
berkserbetOP
I think I get what you're saying
Avatar
DirtyCajunRice | AppDir
secondly
Avatar
berkserbetOP
Let me quickly try that
Avatar
DirtyCajunRice | AppDir
ALL search params are string
doesnt matter that it can be a number. it is a string
so if you want to make pages a number, you need to do that after
thirdly, all searchParams can also be an array of strings
so you need to check for that
Avatar
berkserbetOP
So I updated my app/page.tsx to be this:
import React, { Suspense } from 'react'
import ProductBrowser from './ProductBrowser';

interface Props {
  searchParams: {
    r: string,
    search: string,
    genders: string,
    sizes: string,
    conditions: string,
    countries: string,
    brands: string,
    pages: number
  }
}

export default function Home({searchParams}: Props) {

  return (
    <main>
      <Suspense fallback={<div className="h-screen flex items-center justify-center pb-56"><span className="loading loading-dots loading-lg text-secondary text-center h-screen flex items-center justify-center"></span></div>}>
        <ProductBrowser searchParams={searchParams}/>
      </Suspense>
    </main>
  );
}
Just exporting the searchParams
Avatar
DirtyCajunRice | AppDir
well… you can do it that way too… but you dont need to
Avatar
berkserbetOP
Can I access search params directly from components without passing it around?
Avatar
DirtyCajunRice | AppDir
  export type ProductSearchParams: Record<'r' | 'search' | 'genders' | 'sizes' | 'conditions' | 'countries' | 'brands' | 'pages', string | string[]>
put that in your ProductBrowser.tsx
and change your Props to that
so remove Props interface completely
then also import that into your page.tsx
Avatar
berkserbetOP
Sorry I'm a bit confused - what's the first thing I need to do from my existing codebase?
Avatar
DirtyCajunRice | AppDir
interface Props {
  searchParams: ProductSearchParams
}

export default function Home({searchParams}: Props)
copy that type into your product browser file
Avatar
berkserbetOP
Giving a few errors
Is it supposed to be =
rather than :
Also 'Record' only refers to a type, but is being used as a value here.ts(2693)
Avatar
DirtyCajunRice | AppDir
yeah
im on mobile
Avatar
berkserbetOP
Errors gone
Avatar
DirtyCajunRice | AppDir
show me your product browser page again now updated
Avatar
berkserbetOP
I haven't been able to het it to work yet, trying to understand the page.tsx changes
Avatar
DirtyCajunRice | AppDir
nono
ignore page.tsx
ignore getting it working
just show it
its 3 steps
Avatar
berkserbetOP
import React from 'react'
import { promises as fs } from 'fs';
import path from 'path'
import ProductCards from './ProductCards'
export type ProductSearchParams = Record<'r' | 'search' | 'genders' | 'sizes' | 'conditions' | 'countries' | 'brands' | 'pages', string | string[]>


interface Props {
  search: string,
  selected_subreddits: string,
  selected_genders: string,
  selected_sizes: string,
  selected_conditions: string,
  selected_countries: string,
  selected_brands: string,
  pages: number
}

const ProductBrowser = async ({ search, selected_subreddits, selected_genders, selected_sizes, selected_conditions, selected_countries, selected_brands, pages }: Props) => {

  const filePath = path.join(process.cwd(), 'json/listings.json');
  const file = await fs.readFile(filePath, 'utf8');
  const products = JSON.parse(file)
  
  return (
    <ProductCards products={products} search={search} selected_subreddits={selected_subreddits} selected_genders={selected_genders} selected_sizes={selected_sizes} selected_conditions={selected_conditions} selected_countries={selected_countries} selected_brands={selected_brands} pages={pages}/>
  )
}

export default ProductBrowser
I don't use ProductSearchParams
Avatar
DirtyCajunRice | AppDir
nice. delete Props
Avatar
berkserbetOP
Cool, app still works
Avatar
DirtyCajunRice | AppDir
change the : Props in the component to ProductSearchParams
remove all the selected_ prefix nonsense
Avatar
berkserbetOP
Then what do I pass to ProductCards?
Avatar
DirtyCajunRice | AppDir
the props
Avatar
berkserbetOP
const ProductBrowser = async ({}: ProductSearchParams) => {
  const filePath = path.join(process.cwd(), 'json/listings.json');
  const file = await fs.readFile(filePath, 'utf8');
  const products = JSON.parse(file)
  
  return (
    <ProductCards products={products} search={search} selected_subreddits={selected_subreddits} selected_genders={selected_genders} selected_sizes={selected_sizes} selected_conditions={selected_conditions} selected_countries={selected_countries} selected_brands={selected_brands} pages={pages}/>
  )
}

export default ProductBrowser
Avatar
DirtyCajunRice | AppDir
just named properly
nono
leave the props
just remove the prefix
you added a selected_ prefix for no reason
to all the keys
Avatar
berkserbetOP
import React from 'react'
import { promises as fs } from 'fs';
import path from 'path'
import ProductCards from './ProductCards'
export type ProductSearchParams = Record<'r' | 'search' | 'genders' | 'sizes' | 'conditions' | 'countries' | 'brands' | 'pages', string | string[]>

const ProductBrowser = async ({ r, search, genders, sizes, conditions, countries, brands, pages }: ProductSearchParams) => {
  const filePath = path.join(process.cwd(), 'json/listings.json');
  const file = await fs.readFile(filePath, 'utf8');
  const products = JSON.parse(file)
  
  return (
    <ProductCards products={products} search={search} selected_subreddits={r} selected_genders={genders} selected_sizes={sizes} selected_conditions={conditions} selected_countries={countries} selected_brands={brands} pages={pages}/>
  )
}

export default ProductBrowser
Avatar
DirtyCajunRice | AppDir
yes exactly
and when we are done you can do the same thing to ProductCards
but 1 thing at a time
Avatar
berkserbetOP
Seeing a lot of
Type 'string | string[]' is not assignable to type 'string'.
  Type 'string[]' is not assignable to type 'string'.
Avatar
DirtyCajunRice | AppDir
yep. what i was saying you need to check for. we will get to that
go to page.tsx
import ProductSearchParams
from your other file
Avatar
berkserbetOP
Done
import { ProductSearchParams } from './ProductBrowser';
Avatar
DirtyCajunRice | AppDir
make your searchParams: ProductSearchParams
in your page props def
Avatar
berkserbetOP
import React, { Suspense } from 'react'
import ProductBrowser from './ProductBrowser';
import { ProductSearchParams } from './ProductBrowser';

interface Props {
  ProductSearchParams: {
    r: string,
    search: string,
    genders: string,
    sizes: string,
    conditions: string,
    countries: string,
    brands: string,
    pages: number
  }
}

export default function Home({ProductSearchParams: { r, search, genders, sizes, conditions, countries, brands, pages }}: Props) {

  return (
    <main>
      <Suspense fallback={<div className="h-screen flex items-center justify-center pb-56"><span className="loading loading-dots loading-lg text-secondary text-center h-screen flex items-center justify-center"></span></div>}>
        <ProductBrowser search={search} selected_subreddits={r} selected_genders={genders} selected_sizes={sizes} selected_conditions={conditions} selected_countries={countries} selected_brands={brands} pages={pages}/>
      </Suspense>
    </main>
  );
}
Avatar
DirtyCajunRice | AppDir
then where you have your ProductBrowser in page.tsx just make it <ProductBrowser {...searchParams } />
nono
Avatar
berkserbetOP
I see. 🙂
import React, { Suspense } from 'react'
import ProductBrowser from './ProductBrowser';
import { ProductSearchParams } from './ProductBrowser';

interface Props {
  searchParams: ProductSearchParams
}

export default function Home({searchParams: { r, search, genders, sizes, conditions, countries, brands, pages }}: Props) {

  return (
    <main>
      <Suspense fallback={<div className="h-screen flex items-center justify-center pb-56"><span className="loading loading-dots loading-lg text-secondary text-center h-screen flex items-center justify-center"></span></div>}>
      <ProductBrowser {...searchParams } />
      </Suspense>
    </main>
  );
}
Error is Cannot find name 'searchParams'. Did you mean 'URLSearchParams'?ts(2552)
Avatar
DirtyCajunRice | AppDir
import React, { Suspense } from 'react'
import ProductBrowser from './ProductBrowser';
import { ProductSearchParams } from './ProductBrowser';

interface Props {
  searchParams: ProductSearchParams
}

export default function Home({ searchParams }: Props) {

  return (
    <main>
      <Suspense fallback={<div className="h-screen flex items-center justify-center pb-56"><span className="loading loading-dots loading-lg text-secondary text-center h-screen flex items-center justify-center"></span></div>}>
        <ProductBrowser {…searchParams} />
      </Suspense>
    </main>
  );
}
simple
like this
gotta change those dots tho
iphone fucks up the dots
gotta type them manually haha
Avatar
berkserbetOP
Done!
Works
No errors
Avatar
DirtyCajunRice | AppDir
see how the page has no errors?
magic 😉
now
Avatar
berkserbetOP
Magic
Avatar
DirtyCajunRice | AppDir
now before we move on
where are you going to host this thing
Avatar
berkserbetOP
Vercel I host it on
Avatar
DirtyCajunRice | AppDir
ok. then your fs read is silly and wasteful
and the component doesnt need async nor suspense
Avatar
berkserbetOP
🙂
Avatar
DirtyCajunRice | AppDir
just import the json
Avatar
berkserbetOP
The json can get big
Avatar
DirtyCajunRice | AppDir
doesnt matter
Avatar
berkserbetOP
And changes often
Cool
Avatar
DirtyCajunRice | AppDir
it doesnt change without redeploying
Avatar
berkserbetOP
Correct
Avatar
DirtyCajunRice | AppDir
so its literally the same thing either way
Avatar
berkserbetOP
Trying
import products from '../json/listings.json'?
Avatar
DirtyCajunRice | AppDir
you use cwd
so 1 dot
Avatar
berkserbetOP
VScode autofilled
Avatar
DirtyCajunRice | AppDir
1 dot == same folder 2 dots == parent folder
vscode is caca
😂
Avatar
berkserbetOP
Got it
🙂
Avatar
DirtyCajunRice | AppDir
ok so remove async and just pass that along
also remove suspense from parent as the compnent isnt async so it doesnt do anything
Avatar
berkserbetOP
New page.tsx:
import React, { Suspense } from 'react'
import ProductBrowser from './ProductBrowser';
import { ProductSearchParams } from './ProductBrowser';

interface Props {
  searchParams: ProductSearchParams
}

export default function Home({ searchParams }: Props) {

  return (
    <main>
      <ProductBrowser {...searchParams} />
    </main>
  );
}
Avatar
DirtyCajunRice | AppDir
so clean
Avatar
berkserbetOP
Current ProductBrowser.tsx
import React from 'react'
import ProductCards from './ProductCards'
export type ProductSearchParams = Record<'r' | 'search' | 'genders' | 'sizes' | 'conditions' | 'countries' | 'brands' | 'pages', string | string[]>
import products from './json/listings.json'

const ProductBrowser = ({ r, search, genders, sizes, conditions, countries, brands, pages }: ProductSearchParams) => {
  
  return (
    <ProductCards products={products} search={search} selected_subreddits={r} selected_genders={genders} selected_sizes={sizes} selected_conditions={conditions} selected_countries={countries} selected_brands={brands} pages={pages}/>
  )
}

export default ProductBrowser
Avatar
DirtyCajunRice | AppDir
less clean haha
Avatar
berkserbetOP
I don't even need the productbrowser now I think
Avatar
DirtyCajunRice | AppDir
you do not 🙂
Avatar
berkserbetOP
I can just pass directly
Avatar
DirtyCajunRice | AppDir
so update your Product card params to use the same logic you used for the browser
Avatar
berkserbetOP
On it
So my ProductCards.tsx starts as:
'use client'

import React from 'react'
import Link from 'next/link'
import SearchContainer from './SearchContainer';
import Image from 'next/image'
import { useRouter } from 'next/navigation';
export type ProductSearchParams = Record<'r' | 'search' | 'genders' | 'sizes' | 'conditions' | 'countries' | 'brands' | 'pages', string | string[]>
import products from './json/listings.json'
Avatar
DirtyCajunRice | AppDir
do you wanna make the ProductSearchParams cleaner?
how comfortable are you with typescript
Avatar
berkserbetOP
I do
Not super
Up to you
Avatar
DirtyCajunRice | AppDir
const productSearchParams =  ['r', 'search', 'genders',  'sizes', 'conditions', 'countries', 'brands', 'pages'] as const;
type ProductSearchParam = typeof allSearchParams[number];
export type ProductSearchParams = Record<ProductSearchParam, string | string[]>;
tada
Avatar
berkserbetOP
Amazing!
Avatar
DirtyCajunRice | AppDir
if any of that is confusing, ask
copying without understanding is the worst sin lol
Avatar
berkserbetOP
What is "as const"
Avatar
DirtyCajunRice | AppDir
coding on phone is such a pain lol
Avatar
berkserbetOP
What is it for I mean
Avatar
DirtyCajunRice | AppDir
means its read only and cant be edited
so its “static” basically
you cant infer a type from a mutable array
Avatar
berkserbetOP
What is allSearchParams?
Avatar
DirtyCajunRice | AppDir
only a static (immutable) arrat
Avatar
berkserbetOP
I don't have that variable
Avatar
DirtyCajunRice | AppDir
just a list of the keys we will use, separated for organization purposes
its the first thing
oh
whoops
all should be product
Avatar
berkserbetOP
Got it
Avatar
DirtyCajunRice | AppDir
again. phone -,/
so the type is saying “this type is all the keys of this array”
Avatar
berkserbetOP
So what does the second line do type ProductSearchParam = typeof allSearchParams[number]
Avatar
DirtyCajunRice | AppDir
what i just said
Avatar
berkserbetOP
Is it looping somehow
Avatar
DirtyCajunRice | AppDir
knew you would ask haha
its the typescript way to say “all the items in the array as type options”
Avatar
berkserbetOP
Sorry I don't get it
" as type options"
This part
Avatar
DirtyCajunRice | AppDir
when you hover over it
does it show you the options?
Avatar
berkserbetOP
Image
Like that?
Avatar
DirtyCajunRice | AppDir
yes but hover over the left side
the type itself
Avatar
berkserbetOP
Image
This one?
Avatar
DirtyCajunRice | AppDir
see what it did?
it plucked all the items out of the array and made them “this or this or this” as a type
Avatar
berkserbetOP
But how does it know the type?
We haven't given any
Avatar
DirtyCajunRice | AppDir
we did
we said it is the items in the array
its a new type
of those strings
Avatar
berkserbetOP
Got it
Avatar
DirtyCajunRice | AppDir
“just typescript things”
😂
Avatar
berkserbetOP
🙂
Avatar
DirtyCajunRice | AppDir
and you know what a Record is?
Avatar
berkserbetOP
And I get the third line
Avatar
DirtyCajunRice | AppDir
ok cool
Avatar
berkserbetOP
I don't actually
What is Record
I assumed but not sure
Avatar
DirtyCajunRice | AppDir
a record is just an object, but where you already know all the key and value types
so you dont have to manually define it because it can be inferred
your original one was a “manual” record
this one is “automatic” because we have a key type
Avatar
berkserbetOP
Which is string or array of strings
Right?
Avatar
DirtyCajunRice | AppDir
thats the value type
the key type is the array of productSearchParams
so string…. but “specific”
Avatar
berkserbetOP
Very cool
Avatar
DirtyCajunRice | AppDir
so record says “for each item in the key type, the value type is”
Avatar
berkserbetOP
The next thing I did was import the json
Avatar
DirtyCajunRice | AppDir
yes.
do you have a type for your json?
Avatar
berkserbetOP
import listings from './json/listings.json'

But giving error
Avatar
DirtyCajunRice | AppDir
what error
screenshot your folder/file window
Avatar
berkserbetOP
'listings' is declared but its value is never read.ts(6133)
Cannot find module './json/listings.json' or its corresponding type declarations.ts(2307)
Cannot find seems to be important
Avatar
DirtyCajunRice | AppDir
^
Avatar
berkserbetOP
Image
I think it's in the right spot
Avatar
DirtyCajunRice | AppDir
wait
is app and json at the same level?
Avatar
berkserbetOP
No
Its a level up 🙂
I see
..
Avatar
DirtyCajunRice | AppDir
also
do you have type aliases in tsconfig?
it would help you
not type
path aliases
Avatar
berkserbetOP
{
  "compilerOptions": {
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "paths": {
      "@/*": ["./*"]
    },
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "*/listings.json"],
  "exclude": ["node_modules"]
}
This is what I got
Avatar
DirtyCajunRice | AppDir
yeah
Avatar
berkserbetOP
"baseUrl": "./src",
"paths": {
    "@modules/*": ["rest/modules/*"],
    "@services/*": ["services/*"]
}
Avatar
DirtyCajunRice | AppDir
so you can import smarter
Avatar
berkserbetOP
Found this online
Like that?
Avatar
DirtyCajunRice | AppDir
nono
yours is good
leave it
Avatar
berkserbetOP
Cool
No more error in the import
My json is a list of objects which I define in Cards
Avatar
DirtyCajunRice | AppDir
import Listings from “@/json/listings.json"
you can import this way
with the aliases
much prettier imo anyway haha
Avatar
berkserbetOP
Yup!
No error
Avatar
DirtyCajunRice | AppDir
whats your listings json look like
just show me like the first 20-30 lines
Avatar
berkserbetOP
[
    {
        "title": "Lululemon City Keeper Gloves",
        "brands": [
            "Lululemon"
        ],
        "actions": [
            "Selling"
        ],
        "status": "Active",
        "genders": [
            "Women"
        ],
        "sizes": [],
        "categories": [],
        "price": 29,
        "currency": "USD",
        "countries": [],
        "images": [
            "https://i.redd.it/71paagbg2afc1.jpg",
            "https://i.redd.it/7rfw7gbg2afc1.jpg"
        ],
        "conditions": [
            "New With Tags"
        ],
        "colors": [],
        "shipping_info": "Shipping included.",
        "search_keywords": null,
        "timestamp": 1706489846,
        "source": "Reddit",
        "reddit_subreddit": "lululemonBST",
        "reddit_author": "mrasifs",
        "reddit_post_id": "1adie9g",
        "reddit_thread_id": null,
        "reddit_comment_id": null,
        "listing_number": 0
    },
    {
        "title": "Og Align 25\u201d (4) Dark Forest",
        "brands": [
            "Lululemon"
        ],
        "acti
interface Product {
  title: string;
  brands: string[];
  actions: string[];
  status: string | null;
  genders: string[];
  sizes: string[];
  categories: string[];
  price: number | null;
  currency: string | null;
  countries: string[];
  images: string[];
  conditions: string[];
  colors: string[];
  shipping_info: string | null;
  search_keywords: string | null;
  timestamp: number; // in seconds
  source: string;
  reddit_subreddit: string;
  reddit_author: string;
  reddit_post_id: string | null;
  reddit_thread_id: string | null;
  reddit_comment_id: string | null;
  listing_number: number;
}
Defined here
Avatar
DirtyCajunRice | AppDir
wheres that at
Avatar
berkserbetOP
In ProductCards.tsx
Avatar
DirtyCajunRice | AppDir
make yourself a types folder
so you keep things organized
Avatar
berkserbetOP
But that is the only type
No other one
Avatar
DirtyCajunRice | AppDir
is it? we have already made another type
😉
Avatar
berkserbetOP
allSearchParams?
Yeah
Avatar
DirtyCajunRice | AppDir
mhm
and you will make more.
Avatar
berkserbetOP
Where should I put the types folder?
Avatar
DirtyCajunRice | AppDir
same level as json
outside app
Avatar
berkserbetOP
Created it
Avatar
DirtyCajunRice | AppDir
make a products.ts file
move that type and the 3 lines we made into that file
to keep things organized
then fix imports if needed
Avatar
berkserbetOP
const allSearchParams =  ['r', 'search', 'genders',  'sizes', 'conditions', 'countries', 'brands', 'pages'] as const;
type ProductSearchParam = typeof allSearchParams[number];
export type ProductSearchParams = Record<ProductSearchParam, string | string[]>;

interface Product {
    title: string;
    brands: string[];
    actions: string[];
    status: string | null;
    genders: string[];
    sizes: string[];
    categories: string[];
    price: number | null;
    currency: string | null;
    countries: string[];
    images: string[];
    conditions: string[];
    colors: string[];
    shipping_info: string | null;
    search_keywords: string | null;
    timestamp: number; // in seconds
    source: string;
    reddit_subreddit: string;
    reddit_author: string;
    reddit_post_id: string | null;
    reddit_thread_id: string | null;
    reddit_comment_id: string | null;
    listing_number: number;
}
Thats the whole file
Avatar
DirtyCajunRice | AppDir
dont forget to export Product
Avatar
berkserbetOP
const allSearchParams =  ['r', 'search', 'genders',  'sizes', 'conditions', 'countries', 'brands', 'pages'] as const;
type ProductSearchParam = typeof allSearchParams[number];
export type ProductSearchParams = Record<ProductSearchParam, string | string[]>;

export interface Product {
    title: string;
    brands: string[];
    actions: string[];
    status: string | null;
    genders: string[];
    sizes: string[];
    categories: string[];
    price: number | null;
    currency: string | null;
    countries: string[];
    images: string[];
    conditions: string[];
    colors: string[];
    shipping_info: string | null;
    search_keywords: string | null;
    timestamp: number; // in seconds
    source: string;
    reddit_subreddit: string;
    reddit_author: string;
    reddit_post_id: string | null;
    reddit_thread_id: string | null;
    reddit_comment_id: string | null;
    listing_number: number;
}
Avatar
DirtyCajunRice | AppDir
ok. very nice
Avatar
berkserbetOP
Now trying import
Avatar
DirtyCajunRice | AppDir
@/types/products
import { Product } from
something like that
Avatar
berkserbetOP
My page.tsx:
import React from 'react'
import ProductCards from './ProductCards';
import ProductSearchParams from '@/types/products';

interface Props {
  searchParams: ProductSearchParams
}

export default function Home({ searchParams }: Props) {

  return (
    <main>
      <ProductCards {...searchParams} />
    </main>
  );
}
ProductSearchParams has an error
Module '"/Users/berkserbetcioglu/Code/storefront/types/products"' has no default export. Did you mean to use 'import { ProductSearchParams } from "/Users/berkserbetcioglu/Code/storefront/types/products"' instead?ts(2613)
Fixed
What even is the { } for?
Avatar
DirtyCajunRice | AppDir
good. if you couldnt figure that one out i might have given up on you
Avatar
berkserbetOP
🙂
Avatar
DirtyCajunRice | AppDir
means an export in the file
since you can export many things
Avatar
berkserbetOP
Also done with importing to ProductCard
'use client'

import React from 'react'
import Link from 'next/link'
import SearchContainer from './SearchContainer';
import Image from 'next/image'
import { useRouter } from 'next/navigation';
import products from '@/json/listings.json'
import { Product } from '@/types/products';
Still have this error
Image
Avatar
DirtyCajunRice | AppDir
does it need to be client?
Avatar
berkserbetOP
It has a bunch of product listings that update based on filters
So I think so
Avatar
DirtyCajunRice | AppDir
arent your filters in the url…?
Avatar
berkserbetOP
Yeah
Avatar
DirtyCajunRice | AppDir
so…
not client
Avatar
berkserbetOP
Ah
Avatar
DirtyCajunRice | AppDir
😉
Avatar
berkserbetOP
App broke, but I think I'm on the right track 🙂
I use useRouter()
Avatar
DirtyCajunRice | AppDir
for what
to update the url?
Avatar
berkserbetOP
Yeah
router.push(path, { scroll: false });
Avatar
DirtyCajunRice | AppDir
instead use redirect()
same thing and no use
Avatar
berkserbetOP
Cool
import redirect from 'nextjs-redirect' this one?
Avatar
DirtyCajunRice | AppDir
nono
nextjs/navigation iiirc
this
helpers gotta know the docs
or we would be lost
Avatar
berkserbetOP
Updated
Avatar
DirtyCajunRice | AppDir
whats your productcards look like now
Avatar
berkserbetOP
Unhandled Runtime Error
Error: Event handlers cannot be passed to Client Component props.
  <input name="r" type=... id="r" value=... className=... defaultChecked=... onClick={function}>
                                                                                     ^^^^^^^^^^
If you need interactivity, consider converting part of this to a Client Component.
Avatar
DirtyCajunRice | AppDir
yeah you will change those to a form
Avatar
berkserbetOP
Cool
Avatar
DirtyCajunRice | AppDir
with server action
pause for a sec
lets clean up the search params
Avatar
berkserbetOP
This is the start of my Product Cards
import React from 'react'
import Link from 'next/link'
import SearchContainer from './SearchContainer';
import Image from 'next/image'
import products from '@/json/listings.json'
import { Product } from '@/types/products';
import { redirect } from 'next/navigation'


interface Props {
  products: Product[],
  search: string,
  r: string,
  genders: string,
  sizes: string,
  conditions: string,
  countries: string,
  brands: string,
  pages: number
}

const ProductCards = ({r, search, genders, sizes, conditions, countries, brands, pages}: Props) => {
I know Props can be gone
Avatar
DirtyCajunRice | AppDir
right
in this case tho, we wanna sanitize your props
Avatar
berkserbetOP
const ProductCards = ({r, search, genders, sizes, conditions, countries, brands, pages}: ProductSearchParams) => {
I did this
Avatar
DirtyCajunRice | AppDir
so lets make a helper function above ProductCards
Avatar
berkserbetOP
function sanitize(params: ProductSearchParams) {
  return params
}
?
With like if's in there
Avatar
DirtyCajunRice | AppDir
we can be fancy though
sec
Avatar
berkserbetOP
I like that
Avatar
DirtyCajunRice | AppDir
function sanitize(params: ProductSearchParams) {
  const stringsOnly = Object.fromEntries(Object.entries(params).map(([k, v]) => [k, Array.isArray(v) ? v[0] : v]));
  return {... stringsOnly, pages: Number(stringsOnly.pages)};
}
does that func make sense?
Avatar
berkserbetOP
For some of them I just use comma seperated strings
Then split by the comma to form an array
Is that ok?
Avatar
DirtyCajunRice | AppDir
comma separated is not how nextjs does arrays
so thats fine
to make an array someone would have to do ?pages=2&pages=3
multiple keys turn it into an array
Avatar
berkserbetOP
I can have longish ones
Avatar
DirtyCajunRice | AppDir
thats totally fine
Avatar
berkserbetOP
I don't quite understand
Avatar
DirtyCajunRice | AppDir
so lets work from inside to outside
you know what Object.entries does?
Avatar
berkserbetOP
I don't
Avatar
DirtyCajunRice | AppDir
takes an object, and turns it into an array
each item in the array is an array of [key, value]
Avatar
berkserbetOP
Got it
Avatar
DirtyCajunRice | AppDir
so it would be like
[["pages", "2"], ["r", "poo"]]
Avatar
berkserbetOP
Perfect
Avatar
DirtyCajunRice | AppDir
we then map those values
Avatar
berkserbetOP
Yup
Avatar
DirtyCajunRice | AppDir
and keep the key but check the value
Avatar
berkserbetOP
If array then v[0]
Why is that?
Avatar
DirtyCajunRice | AppDir
yeah. basically say “if you manually put more than 1 key on my site i dont care i want the first one”
Avatar
berkserbetOP
Cool
Avatar
DirtyCajunRice | AppDir
never trust the user
Avatar
berkserbetOP
stringsOnly has an array of arrays now
Avatar
DirtyCajunRice | AppDir
or a fart after age 30
jkjk
Avatar
berkserbetOP
With the first val
Of each key
Avatar
DirtyCajunRice | AppDir
and each value as a string
Avatar
berkserbetOP
Yup, then the last one is noing number conversion - not sure how
Avatar
DirtyCajunRice | AppDir
then because you want pages to be a number, we return a new object that we spread all the keys back in, but override the pages key to a number version
Avatar
berkserbetOP
Oh cool
So little code to accomplish that
Avatar
DirtyCajunRice | AppDir
you could also just do
stringsOnly.pages = Number(stringsOnly.pages)
but i like clean short code
Avatar
berkserbetOP
Yeah for sure
Where do I call sanitize?
Avatar
DirtyCajunRice | AppDir
so heres the fun part
your object spread you have in ProductCards definition
move that INSIDE the component
Avatar
berkserbetOP
🙂 crazy
Avatar
DirtyCajunRice | AppDir
as like
const {put it here} = sanitize(props)
Avatar
berkserbetOP
Got it
Avatar
DirtyCajunRice | AppDir
and your func should just be (props: ProductSearchParams
then you get the same functionality, but clean
Avatar
berkserbetOP
Some errors on the vars:
'const' declarations must be initialized.ts(1155)
Variable 'brands' implicitly has an 'any' type
const ProductCards = (props: ProductSearchParams) => {
  const r, search, genders, sizes, conditions, countries, brands, pages = sanitize(props)
Avatar
DirtyCajunRice | AppDir
nono
inside {}
Avatar
berkserbetOP
Oh
Avatar
DirtyCajunRice | AppDir
its still an object
Avatar
berkserbetOP
Like this?
const ProductCards = (props: ProductSearchParams) => {
  const { r, search, genders, sizes, conditions, countries, brands, pages } = sanitize(props)
Avatar
DirtyCajunRice | AppDir
nice
no errors right?
Avatar
berkserbetOP
I have errors
Property 'r' does not exist on type '{ pages: number; }'.ts(2339)
Avatar
DirtyCajunRice | AppDir
i have emotional errors
Avatar
berkserbetOP
🙂
Avatar
DirtyCajunRice | AppDir
hahahaha
Avatar
berkserbetOP
I appreciate you so greatly
Avatar
DirtyCajunRice | AppDir
show me your sanitize func
Avatar
berkserbetOP
function sanitize(params: ProductSearchParams) {
  const stringsOnly = Object.fromEntries(Object.entries(params).map(([k, v]) => [k, Array.isArray(v) ? v[0] : v]));
  return {... stringsOnly, pages: Number(stringsOnly.pages)};
}
Avatar
DirtyCajunRice | AppDir
ok. we will do this the verbose way
go to your types
and export ProductSearchParam
so we have access to it
Avatar
berkserbetOP
I do that
export type ProductSearchParams = Record<ProductSearchParam, string | string[]>;
Avatar
DirtyCajunRice | AppDir
nope
that is a different type
🙂
hint: no s at the end
Avatar
berkserbetOP
Ah
export type ProductSearchParam = typeof allSearchParams[number];
Avatar
DirtyCajunRice | AppDir
yes
ok so now at the end of your sanitize where you return the object
Avatar
berkserbetOP
I imported it
Current: return {... stringsOnly, pages: Number(stringsOnly.pages)};
Avatar
DirtyCajunRice | AppDir
add this at the end
as Record<Exclude<ProductSearchParam, 'pages'>, string> & { pages: number };
Avatar
berkserbetOP
Addind
Avatar
DirtyCajunRice | AppDir
im ready for the questions
😂
Avatar
berkserbetOP
I get it
Avatar
DirtyCajunRice | AppDir
oh? very good
Avatar
berkserbetOP
Exclude pages mark string
Pages number
Avatar
DirtyCajunRice | AppDir
my man
ok. is the other error now gone?
Avatar
berkserbetOP
Why couldn't we use ProductSearchParams
Error gone
Avatar
DirtyCajunRice | AppDir
Because then we need to use a much fancier typescript type to exclude because that is already a record type
and i cant be assed to type that out on my phone
😅
Avatar
berkserbetOP
Yup!
In my borwser localhost I see a related error
./app/ProductCards.tsx
Error: 
  × cannot reassign to a variable declared with `const`
     ╭─[/Users/berkserbetcioglu/Code/storefront/app/ProductCards.tsx:12:1]
  12 │ }
  13 │ 
  14 │ const ProductCards = (props: ProductSearchParams) => {
  15 │   const { r, search, genders, sizes, conditions, countries, brands, pages } = sanitize(props)
     ·           
So in the declaration of those variables with sanitize
Avatar
DirtyCajunRice | AppDir
are you using those variable names anywhere else?
inside that component
Avatar
berkserbetOP
I likely am
Avatar
DirtyCajunRice | AppDir
if you want to be safe
Avatar
berkserbetOP
I will update
Avatar
DirtyCajunRice | AppDir
you can not destructure it
and save as like. initialParams
then get them via initialParams.whatever
Avatar
berkserbetOP
Ah
Yeah sounds good
const { initialParams } = sanitize(props)
Like that?
  const initialParams = sanitize(props)
Like that
Avatar
DirtyCajunRice | AppDir
yeah exactly
Avatar
berkserbetOP
Cool, worked
Updating vars in file
Avatar
DirtyCajunRice | AppDir
once you change your router.push to redirects, your problem should be gone
Avatar
berkserbetOP
Getting an error with my products variable
I imported like this: import products from '@/json/listings.json'
The error is long:
Argument of type '({ title: string; brands: string[]; actions: string[]; status: string; genders: string[]; sizes: string[]; categories: string[]; price: number; currency: string; countries: string[]; images: string[]; conditions: string[]; ... 10 more ...; listing_number: number; } | ... 29 more ... | { ...; })[]' is not assignable to parameter of type 'Product[]'.
  Type '{ title: string; brands: string[]; actions: string[]; status: string; genders: string[]; sizes: string[]; categories: string[]; price: number; currency: string; countries: string[]; images: string[]; conditions: string[]; ... 10 more ...; listing_number: number; } | ... 29 more ... | { ...; }' is not assignable to type 'Product'.
    Type '{ title: string; brands: string[]; actions: string[]; status: string; genders: string[]; sizes: string[]; categories: string[]; price: number; currency: string; countries: string[]; images: string[]; conditions: string[]; ... 10 more ...; reddit_thread_id?: undefined; }' is not assignable to type 'Product'.
      Types of property 'reddit_thread_id' are incompatible.
        Type 'undefined' is not assignable to type 'string | null'.ts(2345)
Avatar
DirtyCajunRice | AppDir
ah yeah
if the key isnt there, you dont put null
you put ?:
Avatar
berkserbetOP
I either put an empty array if array type or null
Avatar
DirtyCajunRice | AppDir
Types of property 'reddit_thread_id' are incompatible.
        Type 'undefined' is not assignable to type 'string | null'.ts(2345)
according to this, you have an undefined somewhere in that json, but dont have it in your type
Avatar
berkserbetOP
Hmm
Let me check
Avatar
DirtyCajunRice | AppDir
show me your Project interface
Avatar
berkserbetOP
--What is that?--
export interface Product {
    title: string;
    brands: string[];
    actions: string[];
    status: string | null;
    genders: string[];
    sizes: string[];
    categories: string[];
    price: number | null;
    currency: string | null;
    countries: string[];
    images: string[];
    conditions: string[];
    colors: string[];
    shipping_info: string | null;
    search_keywords: string | null;
    timestamp: number; // in seconds
    source: string;
    reddit_subreddit: string;
    reddit_author: string;
    reddit_post_id: string | null;
    reddit_thread_id: string | null;
    reddit_comment_id: string | null;
    listing_number: number;
}
Avatar
DirtyCajunRice | AppDir
this is what i meant
change your reddit_thread_id
to add a ? before:
Avatar
berkserbetOP
?reddit_thread_id: string | null;
Like that?
Avatar
DirtyCajunRice | AppDir
reddit_thread_id?: string | null;
Avatar
berkserbetOP
Done
Avatar
DirtyCajunRice | AppDir
error gone?
Avatar
berkserbetOP
Nope seemingly there will be more with that json
Argument of type '({ title: string; brands: string[]; actions: string[]; status: string; genders: string[]; sizes: string[]; categories: string[]; price: number; currency: string; countries: string[]; images: string[]; conditions: string[]; ... 10 more ...; listing_number: number; } | ... 29 more ... | { ...; })[]' is not assignable to parameter of type 'Product[]'.
  Type '{ title: string; brands: string[]; actions: string[]; status: string; genders: string[]; sizes: string[]; categories: string[]; price: number; currency: string; countries: string[]; images: string[]; conditions: string[]; ... 10 more ...; listing_number: number; } | ... 29 more ... | { ...; }' is not assignable to type 'Product'.
    Type '{ title: string; brands: string[]; actions: string[]; status: string; genders: string[]; sizes: string[]; categories: string[]; price: number; currency: string; countries: string[]; images: string[]; conditions: string[]; ... 10 more ...; reddit_thread_id?: undefined; }' is not assignable to type 'Product'.
      Types of property 'shipping_info' are incompatible.
        Type 'never[]' is not assignable to type 'string'.
I'm not sure what never[] is
But shipping_info is null often
Avatar
DirtyCajunRice | AppDir
basically, you need to match up your json with your type
so go through those 1 by 1 and fix
or fix the type
Avatar
berkserbetOP
But why is it broken now?
Avatar
DirtyCajunRice | AppDir
for that one its adding [] to the shipping_info
Avatar
berkserbetOP
It's the same json
Avatar
DirtyCajunRice | AppDir
yeah your json is wonky haha
doesnt adhere like you thought it did
Avatar
berkserbetOP
Got it, fixing
Done!
🙂
Avatar
DirtyCajunRice | AppDir
so you are set?
Avatar
berkserbetOP
Not quite, just settled errors in VScode
Now the form thing I believe
Error: Event handlers cannot be passed to Client Component props.
  <input name="r" type=... id="r" value=... className=... defaultChecked=... onClick={function}>
                                                                                     ^^^^^^^^^^
If you need interactivity, consider converting part of this to a Client Component.
read throught this
Avatar
berkserbetOP
On it
Avatar
berkserbetOP
Do functions that do redirects need to be client side?
Avatar
DirtyCajunRice | AppDir
nope
redirect() is designed to work in server actions
Avatar
berkserbetOP
Cool, I'm just trying to figure out how to turn my 6 <input>'s into <form>'s
I think
Avatar
DirtyCajunRice | AppDir
dont
turn all into 1 form
Avatar
berkserbetOP
Hmm
Avatar
DirtyCajunRice | AppDir
then you can build the url the same way regardless
Avatar
berkserbetOP
This is what I have for each:
                 <input
                    name="r"
                    type="checkbox"
                    id="r"
                    value={subreddit_name}
                    className="checkbox checkbox-sm"
                    defaultChecked={checkHandler("r", {subreddit_name})}
                    onClick={(e) => handleClick(e.target)}
                  />
Avatar
DirtyCajunRice | AppDir
find the closest parent that contains all the inputs
and add a <form> to wrap it
Avatar
berkserbetOP
But I have 6 seperate dropdowns
Avatar
DirtyCajunRice | AppDir
yes
Avatar
berkserbetOP
Each dropdown has on input for many options
Avatar
DirtyCajunRice | AppDir
i understand 🤣 Its fine
trust.
Avatar
berkserbetOP
I trust 🙂
Doing it now
It will basically be
return (
  <form>
Is that ok?
Avatar
DirtyCajunRice | AppDir
thats fine
what is your handleClick doing btw
just rebuilding the url?
Avatar
berkserbetOP
Yeah
  function handleClick(checkbox: any) {
    if (typeof window !== "undefined") {
      queryParams = new URLSearchParams(window.location.search);
    }

    const checkboxes = document.getElementsByName(checkbox.name);
    let selected: string[] = [];

    checkboxes.forEach((item: any) => {
      if (item.checked) selected.push(item.value);
    });
  
    if (selected.length === 0) {
      queryParams.delete(checkbox.name);
    } else {
      queryParams.set(checkbox.name, selected.join(','));
    }

    queryParams.delete("pages");

    const path = window.location.pathname + "?" + queryParams.toString();
    redirect(path);
  }
Probably badly
Avatar
DirtyCajunRice | AppDir
sokay. we can clean it up.
leave the function, but remove the onClick for all of them
Avatar
berkserbetOP
Sure
Done
Avatar
DirtyCajunRice | AppDir
one question... does the user need to submit at all?
or are you immediately changing the value
Avatar
berkserbetOP
Immediate change
I also have a "Load More..." button that increments page
All others are checkboxes
Avatar
DirtyCajunRice | AppDir
ok. we are gonna make input (JUST INPUT) a client component
make a new file
like... checkbox.tsx
Avatar
berkserbetOP
On it
I have the file
Actuallly
I have a seperate searchbar
It's in a seperate component
With 'use client'
So not only immidiate
But in ProductCards all immidiate
Avatar
DirtyCajunRice | AppDir
yeah thats fine
Avatar
berkserbetOP
Awesome
What do I put in the file?
Avatar
DirtyCajunRice | AppDir
'use client';
export const CheckBox = (props: DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>) => {
  const onClick = (e: any) =>  e.currentTarget.form?.requestSubmit();
  return <input {...props} onClick={onClick} />
}
was typing it out
Avatar
berkserbetOP
Amazing thanks!
Can't find InputHTMLAttributes and DetailedHTMLProps
so trying to figure out import
Avatar
DirtyCajunRice | AppDir
import { DetailedHTMLProps, InputHTMLAttributes } from "react";
Avatar
berkserbetOP
Awesome worked
Avatar
DirtyCajunRice | AppDir
should be a drop-in replacement for your <input>
Avatar
berkserbetOP
Testing!
Avatar
DirtyCajunRice | AppDir
well you still need to make your server action
Avatar
berkserbetOP
So I replace the input with CheckBox?
Avatar
DirtyCajunRice | AppDir
yes
keep all the props the same
just change the tag
Avatar
berkserbetOP
Yup
The onclick is what I need to do now
Avatar
DirtyCajunRice | AppDir
  function submitForm(formData: FormData) {
    'use server';
    console.log("Form Data:", formData);
  }
start with this
<form action={submitForm}>
Avatar
berkserbetOP
Where should I have form?
Avatar
DirtyCajunRice | AppDir
^
Avatar
berkserbetOP
I see
Avatar
DirtyCajunRice | AppDir
you are just adding the action prop
Avatar
berkserbetOP
Yeah
Trying to figure this out:
To use Server Actions, please enable the feature flag in your Next.js config. Read more: https://nextjs.org/docs/app/building-your-application/data-fetching/forms-and-mutations#convention
Avatar
DirtyCajunRice | AppDir
O.o
are you using an old version of next...?
Avatar
berkserbetOP
I didn't think so
Let me check
Next.js v13.5.6
Avatar
DirtyCajunRice | AppDir
yeah old
14.1.0
Avatar
berkserbetOP
I'll update
It's because my node was older
Avatar
DirtyCajunRice | AppDir
XD
Avatar
berkserbetOP
I have an issue when trying to update
Working on it
I seemingly have multiple versions
But next is pointing at an old one
Avatar
DirtyCajunRice | AppDir
let me know when you sort that XD
Avatar
berkserbetOP
I think it worked now
Sorry about that
Avatar
DirtyCajunRice | AppDir
np
multitasking
Avatar
berkserbetOP
Seeing:
× Server actions must be async functions
Should I make it
Avatar
DirtyCajunRice | AppDir
yeah thats true
yes
Avatar
berkserbetOP
Is this wrong?
  async function submitForm(formData: FormData) {
    'use server';
    console.log("Form Data:", formData);
  }
Big red error on browser
Same error
Avatar
DirtyCajunRice | AppDir
yeah thats fine
Avatar
berkserbetOP
Image
Avatar
DirtyCajunRice | AppDir
thats an old error
doesnt show your change
stop and start the dev env
Avatar
berkserbetOP
Ok yeah
Got a runtime error: Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server".
Avatar
DirtyCajunRice | AppDir
did you leave onClick somewhere
Avatar
berkserbetOP
I had left the button
Commenting out
Still same error
Avatar
DirtyCajunRice | AppDir
gotta find where its happening
Avatar
berkserbetOP
 ⨯ Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server".
  <... loader={function} src=... onError=... className=... width=... height=... alt=...>
              ^^^^^^^^^^
    at stringify (<anonymous>)
 ⨯ Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server".
  <... loader={function} src=... onError=... className=... width=... height=... alt=...>
              ^^^^^^^^^^
    at stringify (<anonymous>)
What I do I need to look for?
Avatar
DirtyCajunRice | AppDir
well...
i cant see your file
try commenting out whats in the CheckBox component first
to make sure its not that
Avatar
berkserbetOP
Doesn't seem to be that
Can I send the file?
Avatar
DirtyCajunRice | AppDir
sure
Avatar
berkserbetOP
Image
Avatar
DirtyCajunRice | AppDir
you have stuff that matches
ctrl+f for loader
thats where it is
Avatar
berkserbetOP
Matches what?
Avatar
DirtyCajunRice | AppDir
matches the error
the error is because you are using an image loader
Avatar
berkserbetOP
Ah
Yeah I am
Should I make it client
Avatar
DirtyCajunRice | AppDir
if you want to keep doing that, you need to wrap your image component in a client component file
Avatar
berkserbetOP
Yeah I have to I think
Lots of images in my app
On it
I assume I need to move the loader out too
Avatar
DirtyCajunRice | AppDir
you would move it with the component
ImageKitImage
or whatever
Avatar
berkserbetOP
I'm not quite sure how to wrap it
I created image.tsx
So far have this in it:
'use client'

const imageKitLoader = ({
    src,
    width,
    quality,
  }: {
    src: string;
    width: number;
    quality?: number;
  }) => {
    if (src[0] === '/') src = src.slice(1);
    const params = [`w-${width}`];
    if (quality) {
      params.push(`q-${quality}`);
    }
    const paramsString = params.join(',');
    var urlEndpoint = 'https://ik.imagekit.io/y7y0qqdyl';
    if (urlEndpoint[urlEndpoint.length - 1] === '/')
      urlEndpoint = urlEndpoint.substring(0, urlEndpoint.length - 1);

    if (src.includes("redd.it")) {
      return `${urlEndpoint}/r/${src.split("/").pop()}?tr=${paramsString}`;
    } else if (src.includes("postimg")) {
      return `${urlEndpoint}/postimg/${src.split(".cc/").pop()}?tr=${paramsString}`;
    } else if (src.includes("imgur")) {
      return `${urlEndpoint}/imgur/${src.split("/").pop()}?tr=${paramsString}`;
    } else if (src.includes("https://ik.imagekit.io/y7y0qqdyl/")) {
      return `${src}?tr=${paramsString}`;
    } else {
      return `https://ik.imagekit.io/y7y0qqdyl/Image_not_available_wide.webp?tr=${paramsString}`
    }
  };

export const CustomImage = (
Avatar
DirtyCajunRice | AppDir
export const CustomImage = (props: ComponentProps<typeof Image>) => {
  return <Image {...props} loader={imageKitLoader} />
}
Avatar
berkserbetOP
Great
Now I am getting a different issue with the onError={onImageError}
I think I know how to fix
It doesn't like the onError:
<CustomImage
                          src={image}
                          onError={onImageError} 
                          className="w-full rounded-box object-contain"
                          width={480}
                          height={480} 
                          alt="Item Picture"
                        />
I move the function to the image.tsx
But it didn't work
Fixed!
Now app works
Weird error on left: Error: Hydration failed because the initial UI does not match what was rendered on the server.
Avatar
DirtyCajunRice | AppDir
ignore for now
something to address later
Avatar
berkserbetOP
Cool
So now I gotta update query params
Avatar
DirtyCajunRice | AppDir
yes
but first click a button
and check the logs
so you know what the formData looks like
Avatar
berkserbetOP
Form Data: FormData { [Symbol(state)]: [ { name: 'brands', value: 'Adidas' } ] }
Form Data: FormData {
  [Symbol(state)]: [
    { name: 'conditions', value: 'New With Tags' },
    { name: 'brands', value: 'Adidas' }
  ]
}
Form Data: FormData {
  [Symbol(state)]: [
    { name: 'conditions', value: 'New With Tags' },
    { name: 'conditions', value: 'Used' },
    { name: 'brands', value: 'Adidas' }
  ]
}
So it keeps updating
With new objects of clicks
I get it, works great
Avatar
DirtyCajunRice | AppDir
😄
ok. ill leave you to sort the last bit, as you should have it under control. if you get super stuck though you can ping
Avatar
berkserbetOP
For sure, thank you! it's just redirect right?
Avatar
DirtyCajunRice | AppDir
yep!
Avatar
berkserbetOP
Awesome, will try now. Thank you so much for all the help. Is there anything I can do? I would love to pay for your lunch tomorrow
Avatar
DirtyCajunRice | AppDir
Nope no need
Avatar
berkserbetOP
If you do share, where are you based?
Avatar
DirtyCajunRice | AppDir
Austin TX
Avatar
berkserbetOP
Oh cool
I'm SF
Avatar
berkserbetOP
What is Symbol(state)?
I am trying to understand how to read it: { [Symbol(state)]: [ { name: 'sizes', value: '0' } ] }
Something about [FormData Iterator], not sure I undestand that
Avatar
DirtyCajunRice | AppDir
yeah its not a simple object
it has methods
you can do .get()
and .getAll() or something like that
Avatar
berkserbetOP
Do I need to move my checkHandler?
But then the initialparams wont be accessible
  function checkHandler(checkBoxType: string, checkBoxValue: any) {
    if (typeof window !== "undefined") {
      if (checkBoxType == 'r' && typeof initialParams.r !== "undefined") {
        if (initialParams.r.split(",").includes(checkBoxValue['subreddit_name'])) { return true }
        return false
      }
      if (checkBoxType == 'genders' && typeof initialParams.genders !== "undefined") {
        if (initialParams.genders.split(",").includes(checkBoxValue['gender'])) { return true }
        return false
      }
      if (checkBoxType == 'sizes' && typeof initialParams.sizes !== "undefined") {
        if (initialParams.sizes.split(",").includes(checkBoxValue['size'])) { return true }
        return false
      }
      if (checkBoxType == 'conditions' && typeof initialParams.conditions !== "undefined") {
        if (initialParams.conditions.split(",").includes(checkBoxValue['condition'])) { return true }
        return false
      }
      if (checkBoxType == 'countries' && typeof initialParams.countries !== "undefined") {
        if (initialParams.countries.split(",").includes(checkBoxValue['country'])) { return true }
        return false
      }

      if (checkBoxType == 'brands' && typeof initialParams.brands !== "undefined") {
        if (initialParams.brands.split(",").includes(checkBoxValue['brand'])) { return true }
        return false
      }
      return false
    }
    return false;
  }
Avatar
DirtyCajunRice | AppDir
you dont need that anymore
all that data is in the object
Avatar
berkserbetOP
But they don't stay selected
Avatar
DirtyCajunRice | AppDir
you need to set the value
from your params
Avatar
berkserbetOP
Oh so instead of checkHandler
But why wouldnt checkHandler work
Avatar
DirtyCajunRice | AppDir
because its client side only
which is also why you have hydration errors
Avatar
berkserbetOP
Should I mark it as client
Avatar
DirtyCajunRice | AppDir
no
doesnt need to be client
theres no reason to check window at all in that function
Avatar
berkserbetOP
So how do I mark which checkboxes are checked?
Avatar
DirtyCajunRice | AppDir
fixing your func. sec.
Avatar
berkserbetOP
🙏
Avatar
DirtyCajunRice | AppDir
function checkHandler(param: ProductSearchParam, values: any) {
  if (!initialParams[param] || param === 'pages') {
    return false;
  }
  const options = initialParams[param].split(',')
  switch (param) {
    case "r":
      return options.includes(values['subreddit_name']);
    case "genders":
      return options.includes(values['gender']);
    case "sizes":
      return options.includes(values['size']);
    case "conditions":
      return options.includes(values['condition']);
    case "countries":
      return options.includes(values['country']);
    case "brands":
      return options.includes(values['brand']);
    default:
      return false;
  }
}
Avatar
berkserbetOP
Amazing
Avatar
DirtyCajunRice | AppDir
i have no idea what values is
but... i assume you do
Avatar
berkserbetOP
yeah
It's like sizes: M
value is M
Clothing
Couple errors
Property 'split' does not exist on type 'string | number'.
  Property 'split' does not exist on type 'number'
Pages doesn't go to this one ever
Also Cannot find name 'checkBoxType'. Did you mean 'CheckBox'?ts(2552)
checkBoxType is param now I think
Avatar
DirtyCajunRice | AppDir
oh. do a type check safey on the first if
fixed above
Avatar
berkserbetOP
Amazing, now where is checkHandler called, same place?
Avatar
DirtyCajunRice | AppDir
well, before you had it on default checked
that would be an uncontrolled component
you now want controlled component
so change it to checked instead of defaultChecked
Avatar
berkserbetOP
Seems to be working for that
When I manually enter a param its checked
Avatar
DirtyCajunRice | AppDir
Avatar
berkserbetOP
Now just the submitForm 😄
Avatar
DirtyCajunRice | AppDir
parse the formData, build your url
redirect to it
ezpz
Avatar
berkserbetOP
Yeah how do I parse Symbol(state)
Avatar
DirtyCajunRice | AppDir
you dont need to
Avatar
berkserbetOP
I just need to iterate and append text
How do I get the object to iterate throuch
Avatar
DirtyCajunRice | AppDir
go back to your types/product.ts
export your array
see all this stuff that ended up being super useful later
😄
Avatar
berkserbetOP
const allSearchParams = ['r', 'search', 'genders', 'sizes', 'conditions', 'countries', 'brands', 'pages'] as const; export type ProductSearchParam = typeof allSearchParams[number]; export type ProductSearchParams = Record<ProductSearchParam, string | string[]>; export interface Product { title: string; brands: string[]; actions: string[]; status: string | null; genders: string[]; sizes: string[]; categories: string[]; price: number | null; currency: string | null; countries: string[]; images: string[]; conditions: string[]; colors: string[]; shipping_info: string | null; search_keywords: string | null; timestamp: number; // in seconds source: string; reddit_subreddit: string; reddit_author: string; reddit_post_id: string | null; reddit_thread_id?: string | null; reddit_comment_id: string | null; listing_number: number; }
Avatar
DirtyCajunRice | AppDir
allSearchParams
export it
Avatar
berkserbetOP
Do I export products = Product[]
Product array
Avatar
DirtyCajunRice | AppDir
nah thats just lazy lol
Avatar
berkserbetOP
Then how? 🙂
Avatar
DirtyCajunRice | AppDir
^
export the array
Avatar
berkserbetOP
Ah
Alright
And imported
Isn't there like a one line version to loop and create the query text
I would loop, over list and keep manually appending with queryParams.set(key, value.join(','));
Loop over the formData
Form Data: FormData {
  [Symbol(state)]: [
    { name: 'sizes', value: '4' },
    { name: 'sizes', value: '5' },
    { name: 'sizes', value: '6”' }
  ]
}
I am trying to grab this part:
[
    { name: 'sizes', value: '4' },
    { name: 'sizes', value: '5' },
    { name: 'sizes', value: '6”' }
  ]
Avatar
DirtyCajunRice | AppDir
then in your server action you can do something along the lines of
  const submitForm = async (formData: FormData) => {
    const newSearchParams = new URLSearchParams();
    allSearchParams.forEach(p => {
      const opts = formData.getAll(p)
      if (opts.length) {
        newSearchParams.append(p, opts.join(","));
      }
    })
    redirect(`/current/url/path?${newSearchParams.toString()}`)
  }
Avatar
berkserbetOP
Nice!
Added 'use client'
Avatar
DirtyCajunRice | AppDir
...
you mean 'use server'
Avatar
berkserbetOP
Yeah sorry
Avatar
DirtyCajunRice | AppDir
ok phew
Avatar
berkserbetOP
  const submitForm = async (formData: FormData) => {
    'use server';
    const newSearchParams = new URLSearchParams();
    allSearchParams.forEach(p => {
      const opts = formData.getAll(p)
      if (opts.length) {
        newSearchParams.append(p, opts.join(","));
      }
    })
    redirect(`/current/url/path?${newSearchParams.toString()}`)
  }
But still getting

Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server".
  <form action={function} children=...>
How do you color like this?
Avatar
DirtyCajunRice | AppDir
add ts after the 3 backticks
```ts
Avatar
berkserbetOP
oh cool
Avatar
DirtyCajunRice | AppDir
that doesnt really make any sense
you can move it to a server actions file
then move the 'use server' to the top of the file
and import it
see if it still does it
Avatar
berkserbetOP
Sure
This doesn't throw an error:
async function submitForm(formData: FormData) {
    'use server';
    const newSearchParams = new URLSearchParams();
    allSearchParams.forEach(p => {
      const opts = formData.getAll(p)
      if (opts.length) {
        newSearchParams.append(p, opts.join(","));
      }
    })
    redirect(`/current/url/path?${newSearchParams.toString()}`)
  }
Avatar
DirtyCajunRice | AppDir
:pepeCry:
they hate my es6
Avatar
berkserbetOP
But now no matter what I click my params are Initial Params: { r: 'url', pages: NaN }
r=url
Avatar
DirtyCajunRice | AppDir
did you change the redirect to your actual path
Avatar
berkserbetOP
🙂
Avatar
DirtyCajunRice | AppDir
:facepalm:
haha
Avatar
berkserbetOP
Does redirect allow you to not scroll
Avatar
DirtyCajunRice | AppDir
i... i actually have no idea
Avatar
berkserbetOP
The app is acting weird
Like the seach bar moves around after every button click
Then goes to it's normal place
Avatar
DirtyCajunRice | AppDir
that would be your client components not having proper css
which is a whole different issue
😄
Avatar
berkserbetOP
But it gets to the right place
Avatar
DirtyCajunRice | AppDir
prerender vs hydration
prerender is only server known stuff
hydration is client side and final state
Avatar
berkserbetOP
Are the hydration errors easy to fix?
Avatar
DirtyCajunRice | AppDir
once you understand what causes hydration errors in the first place, yes absolutely.
Avatar
berkserbetOP
So how can I get my app to work as well as it used to now?
Avatar
DirtyCajunRice | AppDir
you have now acended
Avatar
berkserbetOP
🙂
Seems I need to learn a few more things
Avatar
DirtyCajunRice | AppDir
this will - for better or worse - make you understand the importance of having the correct data on the server so that it matches and is not modified by the client
search around your code and check for anything that modifies how things look via a useEffect
thats your first "oh shit i see"
Avatar
berkserbetOP
useEffect adds animation?
Avatar
DirtyCajunRice | AppDir
useEffect adds client javascript logic
of any kind
Avatar
berkserbetOP
But why do I want that?
Like my searchbar only has: flex items-center justify-center
Why does it move around so much
Avatar
DirtyCajunRice | AppDir
couple things can cause it.
1. you have client css-in-js somewhere.
2. you have values not being populated until client render
3. you have css that does not have stringent bounds causing it to grow or shrink until the item that changes where it flows to appears.
Avatar
berkserbetOP
css-in-js is a function that returns an item with css?
Avatar
DirtyCajunRice | AppDir
not necessarily. Its css that is defined dynamically via javascript
vs static classes / properties
Avatar
berkserbetOP
My search looks like this:
'use client'
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
import { useState } from 'react'

const SearchContainer = () => {

    // NextJs Navigation
    const router = useRouter()
    const searchParams = useSearchParams()
    const pathname = usePathname()
    let queryParams: URLSearchParams;

    // React States
    const [search, setSearch] = useState(searchParams.get('search') || '')

    // Handle Submitting
    const handleSubmit = (e: any) => {
        e.preventDefault()
        if (typeof window !== "undefined") {
          queryParams = new URLSearchParams(window.location.search);
        }

        queryParams.delete("pages");
        if (search === '') {
            queryParams.delete('search');
        } else {
            queryParams.set('search', search);
        }
        const path = window.location.pathname + "?" + queryParams.toString();
        router.push(path);
    }

    const removeSearch = (e: any) => {
        router.push(pathname)
        return
    }

    return (
        <form className='flex items-center justify-center'>
            <input className='border border-gray-400 rounded-md p-1' placeholder='Search' onChange={(e) => setSearch(e.target.value)} defaultValue={search}/>
            <button className='border border-gray-400 rounded-md p-1 m-2 hover:bg-blue-100' type='submit' onClick={handleSubmit}>🔍</button>
            <button type='submit' onClick={removeSearch}>✖️</button>
        </form>
    )
}

export default SearchContainer
Does it just take time to run this one the client side each time
Avatar
DirtyCajunRice | AppDir
record what its doing. its anyones guess without being able to visualize the CLS
Avatar
berkserbetOP
CLS is Cumulative Layout Shift?
Avatar
DirtyCajunRice | AppDir
Exactly 🙂
Avatar
berkserbetOP
For the button that updates the pages number, do I need to create a new button component like the checkbox?
Avatar
DirtyCajunRice | AppDir
no
that one you just remove onClick
and instead use type="submit" action={}
(give it its own action so it can do the unique thing)
Avatar
berkserbetOP
I use to use:
  function handlePageClick() {
    if (typeof window !== "undefined") {
      queryParams = new URLSearchParams(window.location.search);
    }

    initialParams.pages = parseInt(queryParams.get("pages") || "1");
    let nextPage: number = initialParams.pages + 1;
    queryParams.set("pages", nextPage.toString());

    const path = window.location.pathname + "?" + queryParams.toString();
    redirect(path);
  }
Is action a function?
Avatar
DirtyCajunRice | AppDir
action is a server action function
like the form one we made
Avatar
berkserbetOP
To test this I created:
  async function updatePage() {
    'use server';
    console.log("Update Page.")
  } 


The button I made: <button className="btn btn-accent btn-outline btn-lg" type="submit" action={updatePage()}>Load More...</button>

But now my button doesn't appear. And action shows this error:
Type '{ children: string; className: string; type: "submit"; action: Promise<void>; }' is not assignable to type 'DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>'.
  Property 'action' does not exist on type 'DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>'
Image
Avatar
DirtyCajunRice | AppDir
you dont add the () for actions
you are passing the definition of the function, not the function execution
Avatar
berkserbetOP
So like this: action={updatePage}
It gives the same error
Avatar
DirtyCajunRice | AppDir
oh i forgot. when on a button its formAction not action
Avatar
berkserbetOP
Cool!
Avatar
berkserbetOP
When I manually add a pages query param it just gets deleted
I tried this:
  async function updatePage(formData: FormData) {
    'use server';
    console.log("Update Page.")
    console.log("Form Data:", formData)
    const newSearchParams = new URLSearchParams();
    allSearchParams.forEach(p => {
      const opts = formData.getAll(p)
      if (opts.length) {
        console.log("Form Data:", p, opts.join(","));
        newSearchParams.append(p, opts.join(","));
      }
    })

    newSearchParams.set("pages", (parseInt(newSearchParams.get("pages") || "1") + 1).toString())

    redirect(`?${newSearchParams.toString()}`)
  }
  
But pages is never present in allSearchParams
Avatar
DirtyCajunRice | AppDir
mmmm
what do you mean when you manually add it gets delete
Avatar
berkserbetOP
I add ?pages=2
Then I choose a filter, then it's lost
Avatar
DirtyCajunRice | AppDir
there it is
you left critical info out 😉
in the other action we are ignoring pages
you need to now handle pages separately
post that other action
Avatar
berkserbetOP
This one?
  async function submitForm(formData: FormData) {
    'use server';
    const newSearchParams = new URLSearchParams();
    allSearchParams.forEach(p => {
      const opts = formData.getAll(p)
      if (opts.length) {
        console.log("Form Data:", p, opts.join(","));
        newSearchParams.append(p, opts.join(","));
      }
    })
    redirect(`?${newSearchParams.toString()}`)
  }
Avatar
DirtyCajunRice | AppDir
yeah. so…. the pages part is completely missed here
since there is no “input” for it
This one?
  async function submitForm(formData: FormData) {
    'use server';
    const newSearchParams = new URLSearchParams();
    allSearchParams.forEach(p => {
      const opts = formData.getAll(p)
      if (opts.length) {
        console.log("Form Data:", p, opts.join(","));
        newSearchParams.append(p, opts.join(","));
      }
    })
    if (initialParams.pages) {
      newSearchParams.append("pages", initialParams.pages.toString())
    }
    redirect(`?${newSearchParams.toString()}`)
  }
see what i mean?
Avatar
berkserbetOP
Yeah unique case
It is still overwritten when I click a checkbox
Actually I want it to be overwritten
Avatar
DirtyCajunRice | AppDir
so… good… or bad… haha
Avatar
berkserbetOP
Perfect
Do you know why my build might be failing?
Error occurred prerendering page "/". Read more: https://nextjs.org/docs/messages/prerender-error
 ✓ Generating static pages (6/6) 

> Export encountered errors on following paths:
    /page: /
Avatar
DirtyCajunRice | AppDir
not with just that info
Avatar
berkserbetOP
This is all it gives me:
 ✓ Collecting page data    
   Generating static pages (4/6)  [=   ] 

 ⨯ useSearchParams() should be wrapped in a suspense boundary at page "/". Read more: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout

Error occurred prerendering page "/". Read more: https://nextjs.org/docs/messages/prerender-error
 ✓ Generating static pages (6/6) 

> Export encountered errors on following paths:
    /page: /
Avatar
DirtyCajunRice | AppDir
may seem like nothing to you… but that is very helpful info
Avatar
berkserbetOP
🙂
useSearchParams
I see
Avatar
DirtyCajunRice | AppDir
😉
Avatar
berkserbetOP
Part of this component
'use client'
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
import { useState } from 'react'

const SearchContainer = () => {

    // NextJs Navigation
    const router = useRouter()
    const searchParams = useSearchParams()
    const pathname = usePathname()
    let queryParams: URLSearchParams;

    // React States
    const [search, setSearch] = useState(searchParams.get('search') || '')

    // Handle Submitting
    const handleSubmit = (e: any) => {
        e.preventDefault()
        if (typeof window !== "undefined") {
          queryParams = new URLSearchParams(window.location.search);
        }

        queryParams.delete("pages");
        if (search === '') {
            queryParams.delete('search');
        } else {
            queryParams.set('search', search);
        }
        const path = window.location.pathname + "?" + queryParams.toString();
        router.push(path);
    }

    const removeSearch = (e: any) => {
        router.push(pathname)
        return
    }

    return (
        <form className='flex items-center justify-center'>
            <input className='border border-gray-400 rounded-md p-1' placeholder='Search' onChange={(e) => setSearch(e.target.value)} defaultValue={search}/>
            <button className='border border-gray-400 rounded-md p-1 m-2 hover:bg-blue-100' type='submit' onClick={handleSubmit}>🔍</button>
            <button type='submit' onClick={removeSearch}>✖️</button>
        </form>
    )
}

export default SearchContainer
Avatar
DirtyCajunRice | AppDir
show where you are using that
Avatar
berkserbetOP
I call it in ProductCards.tsx
Avatar
DirtyCajunRice | AppDir
wrap it in a <Suspense></Suspense>
Avatar
berkserbetOP
In the return
Avatar
DirtyCajunRice | AppDir
but you now know you can probably convert that to a server component in the future
Avatar
berkserbetOP
Fixed! Thank you
Yeah, but I am not sure how to deal with something like a searchbar yet
Avatar
DirtyCajunRice | AppDir
sokay. something to use to learn in your own. gotta have goals
Avatar
berkserbetOP
Yup 🙂
Found one last important issue
My search query is deleted when checkbox is clicked
I will try to solve it like the page example
This doesn't work unfortunately:
  async function submitForm(formData: FormData) {
    'use server';
    const newSearchParams = new URLSearchParams();
    allSearchParams.forEach(p => {
      const opts = formData.getAll(p)
      if (opts.length) {
        console.log("Form Data:", p, opts.join(","));
        newSearchParams.append(p, opts.join(","));
      }
    })
    if (initialParams.search) {
      newSearchParams.append("search", initialParams.search.toString())
    }
    redirect(`?${newSearchParams.toString()}`)
  }
Worked without tostring()
Avatar
DirtyCajunRice | AppDir
that one isnt a number so that would make sense haha
Avatar
berkserbetOP
Yup
Does it have to rerender the page every time I click a checkbox? I want to make it so the user can just keep clicking
Avatar
DirtyCajunRice | AppDir
as a server action yes, but it should not be noticeable in production
Avatar
berkserbetOP
But the dropdown closes
Avatar
DirtyCajunRice | AppDir
whos fault is that 😁
manage the state of the dropdown
you can do it in another url param, or extract that to a client component
Avatar
berkserbetOP
I guess so, but this stuff just worked on the client side
Avatar
DirtyCajunRice | AppDir
is the ui “just working” any good if the result is that it doesnt do shit? (hence the beginning of this thread)
Avatar
berkserbetOP
Thats true
Thanks for all the help today, will likely come back for more soon 😄
Avatar
DirtyCajunRice | AppDir
it may seem like more work at first, but you are getting granular control over your ui
np man. im gonna pass out soon its late
Avatar
berkserbetOP
So I have one last major issue to tackle. Everything works fine in dev, but in build none of my query parameters do anything. It's like they don't exist and they delete existing query params each time
Also, I was thinking - since I serve users a bunch of images each time, won't I have high bandwidth costs when all of those are server side?
I see a "ReferenceError: window is not defined" in the logs
ReferenceError: window is not defined
    at n (/Users/berkserbetcioglu/Code/storefront/.next/server/app/[...r]/page.js:1:2996)
    at nM (/Users/berkserbetcioglu/Code/storefront/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:47419)
    at nN (/Users/berkserbetcioglu/Code/storefront/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:64674)
    at nI (/Users/berkserbetcioglu/Code/storefront/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:46806)
    at nM (/Users/berkserbetcioglu/Code/storefront/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:47570)
    at nM (/Users/berkserbetcioglu/Code/storefront/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:61663)
    at nN (/Users/berkserbetcioglu/Code/storefront/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:64674)
    at nB (/Users/berkserbetcioglu/Code/storefront/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:67657)
    at nF (/Users/berkserbetcioglu/Code/storefront/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:66824)
    at nN (/Users/berkserbetcioglu/Code/storefront/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:64990)
Avatar
berkserbetOP
Marking this as done to move to a new thread
Avatar
berkserbetOP
Thanks for the help!
Answer