How to map large data in nextjs when in viewport? - javascript

I want to make make a dropdown where a user can select an erc20 token from a tokenlist in Nextjs.
I tried a regular mapping function on the token list but then the site doesn't respond and is very slow because the tokenlist.json. I would like to render the data when in viewport. How can I achieve this?
I would like to make it fast, like in the token select modal in
Uniswap
I used nextjs Image and this loads the token image when in view but it is still slow because it needs to render the token name and symbol
This is how I fetch the tokenlist and render it:
import { Fragment, useEffect, useState } from 'react';
import { Combobox, Transition } from '#headlessui/react';
import { CheckIcon, SelectorIcon } from '#heroicons/react/solid';
import { PlusSmIcon } from '#heroicons/react/outline';
import axios from 'axios';
import tokensJson from '../web3/tokens.json';
import Image from 'next/image';
export default function SelectErc20() {
const [selected, setSelected] = useState(tokensJson.tokens[0]);
const [tokenlist, setTokenlist] = useState([]);
const [query, setQuery] = useState('');
const filteredTokens =
query === ''
? tokenlist
: tokenlist.filter((token) =>
token.name
.toLowerCase()
.replace(/\s+/g, '')
.includes(query.toLowerCase().replace(/\s+/g, ''))
);
useEffect(() => {
axios
.get('https://tokens.coingecko.com/uniswap/all.json')
.then((res) => {
setTokenlist(res.data.tokens);
})
.catch(setTokenlist(tokensJson.tokens));
}, []);
return (
<div className="flex items-center space-x-3">
<img src={selected.logoURI} alt="token" className="h-6 w-6" />
<div className="w-64">
<Combobox value={selected} onChange={setSelected}>
<div className="relative mt-1">
<div className="relative w-full cursor-default overflow-hidden rounded-lg bg-white text-left shadow-md focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-emerald-300 sm:text-sm">
<Combobox.Input
className="w-full border-none py-2 pl-3 pr-10 text-sm leading-5 text-gray-900 focus:ring-0"
displayValue={(token) => token.name}
onChange={(event) => setQuery(event.target.value)}
/>
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2">
<SelectorIcon
className="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</Combobox.Button>
</div>
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Combobox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
<a
href="#"
className="relative mb-3 flex select-none items-center space-x-3 py-2 px-4 text-gray-700 hover:bg-neutral-100"
>
<PlusSmIcon className="h-5 w-5" />
<span>Add custom token</span>
</a>
{filteredTokens.length === 0 && query !== '' ? (
<div className="relative select-none py-2 px-4 text-gray-700">
<span>Nothing found..</span>
</div>
) : (
filteredTokens.map((token) => (
<Combobox.Option
key={token.address}
className={({ active }) =>
`relative cursor-default select-none py-2 pl-10 pr-4 ${
active ? 'bg-emerald-600 text-white' : 'text-gray-900'
}`
}
value={token}
>
{({ selected, active }) => (
<div className="flex items-center justify-between">
<div className="flex items-center truncate">
<Image
src={token.logoURI}
alt={token.name}
width="24"
height="24"
className="mr-3"
/>
<span
className={`block truncate ${
selected ? 'font-medium' : 'font-normal'
}`}
>
{token.name}
</span>
</div>
<span
className={`block text-xs text-gray-400 ${
selected ? 'font-medium' : 'font-normal'
} ${active ? 'text-white' : null}`}
>
{token.symbol}
</span>
{selected ? (
<span
className={`absolute inset-y-0 left-0 flex items-center pl-3 ${
active ? 'text-white' : 'text-emerald-600'
}`}
>
<CheckIcon
className="h-5 w-5"
aria-hidden="true"
/>
</span>
) : null}
</div>
)}
</Combobox.Option>
))
)}
</Combobox.Options>
</Transition>
</div>
</Combobox>
</div>
</div>
);
}

It's because you're rendering too much HTML node, your navigator can't paint it.
In order to do what you need, you must use what we call a 'virtual list'.
There are few libraries to virtualize, you're not the first.
Look at for exemple React Window

Related

How to pass the "isActive" state given by NavLink (in react-router v6.8.1) to its children's className? [duplicate]

This question already has an answer here:
React NavLink change sub element according to isActive
(1 answer)
Closed 10 days ago.
I am unable to pass the isActive state from the Navlink to its children's className (item.icon and span).
<ul className="flex flex-col gap-2 px-3">
{sidebarLinks.map((item) => {
return (
<li key={item.name}>
<NavLink
to={item.href}
className={({ isActive }) => isActive
? "group flex cursor-pointer items-center gap-3 rounded-md bg-gray-200 px-3 py-2 transition-all"
: "group flex cursor-pointer items-center gap-3 rounded-md px-3 py-2 transition-all hover:bg-gray-200 "
}
>
{/* <item.icon
className={`h-5 stroke-gray-400 stroke-2 group-hover:stroke-blue-700`}
/> */}
{/* -------------------PROBLEM STARTS IS FROM HERE----------------------*/}
<item.icon
className={`h-5 stroke-2 ${({ isActive }) => isActive
? "stroke-blue-700"
: "stroke-gray-400"}`
}
/>
<span
className={`font-semibold text-gray-700 group-hover:text-gray-900 ${
({ isActive, }) => (isActive ? "text-red-800" : "text-green-700")
}`}
>
{item.name}
</span>
</NavLink>
</li>
);
})}
</ul>
This is what is shows on inspecting:
I tried to store the isActive state to a new state variable, but react denies it and throws error.
I have also tried to store the boolean value of it in some variable. But I am unable to do so either.
What I am expecting is to carry forward the isActive state from the parent NavLink to its children elements (an icon and a span).
Please correct me if I am doing something wrong.
you can't use isActive in span and item.icon className props like NaveLisk's className prop,
but you can use this method :
<ul className="flex flex-col gap-2 px-3">
{sidebarLinks.map((item) => {
return (
<li key={item.name}>
<NavLink
to={item.href}
className={({ isActive }) =>
isActive
? "group flex cursor-pointer items-center gap-3 rounded-md bg-gray-200 px-3 py-2 transition-all"
: "group flex cursor-pointer items-center gap-3 rounded-md px-3 py-2 transition-all hover:bg-gray-200 "
}
>{({isActive})=>{
return (<>
<item.icon
className={ isActive ? "h-5 stroke-2 stroke-blue-700" : "stroke-gray-400"}
/>
<span
className={`font-semibold text-gray-700 group-hover:text-gray-900 ${isActive ? "text-red-800" : "text-green-700"}`}
>{item.name}</span>
</>)
}}
</NavLink>
</li>
);
})}
</ul>
i send this function as NavLink Children :
{({isActive})=>{
return (<>
<item.icon
className={ isActive ? "h-5 stroke-2 stroke-blue-700" : "stroke-gray-400"}
/>
<span
className={`font-semibold text-gray-700 group-hover:text-gray-900 ${isActive ? "text-red-800" : "text-green-700"}`}
>{item.name}</span>
</>)
}}

Input Field in NextJS causing "Hydration Error and Did not expect server HTML to contain a <div> in <form>" but only in Chrome and only on my computer

as the title states I am creating a website using NextJS styled with TailwindCSS. However, there is a small and annoying issue with my code.
Here is my code below for the MainContent of the page:
"use client"
import React, {useState} from 'react';
export default function MainContent(){
const [eventTerm, setSearchTerm] = useState('');
const [zipCode, setZipCode] = useState('');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSearchTerm(event.currentTarget.value);
}
const handleZipChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setZipCode(event.currentTarget.value);
}
const handleSubmit = (event: React.ChangeEvent <HTMLButtonElement>) => {
event.preventDefault();
// Perform search functionality here
console.log(eventTerm);
console.log(zipCode);
}
return (
<main>
<div className = "max-w-7xl mx-5 px-5 sm:px-6 lg:px-8 my-auto py-9 mt-20 text-center">
<h1 className = "font-rowdies md:text-7xl text-white sm:text-center md:text-left my-3">Test, <br/> Test </h1>
<div className = "my-auto py-5">
<small className = "font-rowdies md:text-3xl text-white md:block md:text-left my-1 flex-wrap sm:text-2xl break-words">Test</small>
<div className="my-auto py-5 md:text-left">
<form className = "relative rounded-lg bg-transparent">
<input type="text" placeholder="Find a Service" defaultValue={eventTerm} onChange={handleChange} className="form-input mr-2 py-2 px-10 rounded-md leading-5 focus:outline-none focus:shadow-outline-indigo-500 w-800 text-center font-rowdies"/>
<input type="text" placeholder="Zip Code" defaultValue={zipCode} onChange={handleZipChange} className="form-input py-2 rounded-md leading-5 focus:outline-none focus:shadow-outline-indigo-500 w-21 text-center hidden md:inline font-rowdies"/>
<button className = "bg-gray-900 text-white ml-2 px-3 py-2 rounded-md text-l font-rowdies hover:text-secondary hover:bg-white">Lets Go!</button>
</form>
</div>
</div>
</div>
</main>
)
}
The errors I am getting in chrome are:
Error: Hydration failed because the initial UI does not match what was rendered on the server.
Warning: Did not expect server HTML to contain a in .
The issue seems to stem from this line only after refreshing the page not on the initial load
<input type="text" placeholder="Zip Code" defaultValue={zipCode} onChange={handleZipChange} className="form-input py-2 rounded-md leading-5 focus:outline-none focus:shadow-outline-indigo-500 w-21 text-center hidden md:inline font-rowdies"/>
I say this because whenever I delete this line the 2 errors go away and my website works just fine.
In addition to this, I sent my friend the file and it ran fine on his computer. Stuff that I tried included checking that my dependencies were up-to-date and clearing my cache and cookies as well as other things with my code.
Any help would be greatly appreciated. I will also add a few more code snippets as well as a directory structure image.
Thanks!
HomePage.tsx
import React, { MouseEventHandler, useState } from 'react';
import { Disclosure, Menu, Transition } from '#headlessui/react'
import '../styles/globals.css'
import MainContent from './MainContent';
export default function (){
const [isOpen, setIsOpen] = useState(false);
return (
<>
<header>
<div className = "items-center">
<nav className = "justify-between bg-secondary">
<div className = "mx-5 px-5 sm:px-6">
<div className = "relative flex items-center justify-between h-16">
<div className = "flex flex-grow-1 flex-shrink-1">
Portfolio
</div>
<div className = "hidden md:block items-end justify-end">
<div className = "ml-10 flex items-end space-x-4">
<button className = "bg-primary text-white px-3 py-2 rounded-md text-l font-rowdies hover:text-primary hover:bg-secondary">Join as a Pro</button>
Log In
Sign Up
About Us
</div>
</div>
<div className="-mr-2 flex md:hidden place-content-center">
<button
onClick={() => setIsOpen(!isOpen)}
type="button"
className="bg-gray-900 inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white"
aria-controls="mobile-menu"
aria-expanded="false"
>
<span className="sr-only">Open main menu</span>
{!isOpen ? (
<svg
className="block h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
) : (
<svg
className="block h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
)}
</button>
</div>
</div>
</div>
<Transition
show={isOpen}
enter="transition ease-out duration-1000 transform"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="transition ease-in duration-75 transform"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
{(ref) => (
<div className="md:hidden text-center bg-secondary" id="mobile-menu">
<div ref={ref} className="px-2 pt-2 pb-3 space-y-1 sm:px-3">
Log In
Sign Up
About Us
<button className = "bg-primary text-white px-3 py-2 rounded-md text-l font-rowdies block mx-auto hover:text-primary hover:bg-secondary">Join as a Pro</button>
</div>
</div>
)}
</Transition>
</nav>
</div>
</header>
<MainContent />
</>
)
}
Page.tsx
"use client";
import Image from 'next/image'
import { Inter } from '#next/font/google'
import styles from './page.module.css'
const inter = Inter({ subsets: ['latin'] })
import { useRouter } from "next/navigation";
import Homepage from '#/components/Homepage';
import MainContent from '#/components/MainContent';
import { Main } from 'next/document';
export default function Home() {
return (
<>
<Homepage />
</>
)
}
Image of all my dependencies and their versions
When I disabled the LastPass browser extension the problem went away. LastPass injects HTML post-load that throws off the comparison between SSR and client.

React context throwing TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))

I'm getting the error:
TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))
whenever I try to manage state from useContext. The idea here is to allow for "tokens" to be initialised at [] on page load, then when set in the TokenListBox component, it is subsequently updated in TokenProviderContext.
TokenProviderContext.tsx:
const TokenProviderContext = React.createContext<any>([]);
export const TokenProvider = ({
children,
}: {
children:
| ReactElement<React.ReactNode, string | JSXElementConstructor<unknown>>[]
| ReactElement<React.ReactNode, string | JSXElementConstructor<unknown>>;
}) => {
const [selectedTokens, setSelectedTokens] = useState<IToken[]>(sampleTokenList);
const contextValue = useMemo(
() => ({
selectedTokens,
setSelectedTokens,
}),
[selectedTokens, setSelectedTokens],
);
return <TokenProviderContext.Provider value={contextValue}>{children}</TokenProviderContext.Provider>;
};
export const useTokenProvider = () => useContext(TokenProviderContext);
TokenListBox.tsx:
export default function TokenListBox({ tokenList }: { tokenList: IToken[] }) {
const [selectedTokens, setSelectedTokens] = useTokenProvider();
useEffect(() => {
if (!selectedTokens) {
setSelectedTokens([]);
}
}, [selectedTokens, setSelectedTokens]);
return (
<Listbox value={selectedTokens} onChange={setSelectedTokens} multiple>
{({ open }) => (
<>
<div className="relative mt-1">
<Listbox.Button
className="relative w-full cursor-default rounded-md border border-gray-300 bg-white
py-2 pl-3 pr-10 text-left shadow-sm focus:border-sky-500 focus:outline-none focus:ring-1 focus:ring-sky-500
sm:text-sm"
>
<span className="flex items-center">
<span className="block truncate">Select Tokens</span>
</span>
<span className="pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2">
<ChevronUpDownIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</span>
</Listbox.Button>
<Transition
show={open}
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
{tokenList.length > 0 && (
<Listbox.Options
className="absolute z-10 mt-1 max-h-56 w-full overflow-auto rounded-md bg-white py-1
text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
>
{tokenList.map((token) => (
<Listbox.Option
key={token.symbol}
className={({ active }) =>
classNames(
active ? 'text-white bg-sky-600' : 'text-gray-900',
'relative cursor-default select-none py-2 pl-3 pr-9',
)
}
value={token.name}
>
{({ selected, active }) => (
<>
<div className="flex items-center">
<img src={token.iconSrcUrl} alt="" className="h-6 w-6 flex-shrink-0 rounded-full" />
<span
className={classNames(selected ? 'font-semibold' : 'font-normal', 'ml-3 block truncate')}
>
{token.name}
</span>
</div>
{selected ? (
<span
className={classNames(
active ? 'text-white' : 'text-sky-600',
'absolute inset-y-0 right-0 flex items-center pr-4',
)}
>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
) : null}
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
)}
</Transition>
</div>
</>
)}
</Listbox>
);
}
When you call useTokenProvider() you would get as result contextValue, which an object not an array, hence the error you are getting.
Assuming TokenListBox is wrapped in TokenProvider, this would work:
const {selectedTokens, setSelectedTokens} = useTokenProvider();

REACT onMouseEnter/onMouseLeave PopUp flickering loop

I'm building a button that shows a PopUp when hovered. To achieve this, I'm using the state "activeToolTip" that turns true "onMouseEnter" / false "onMouseLeave", and in turn renders the PopUp ({activeToolTip ? ( <>popUp<> : null)).
However, when hovering the button, the PopUp flickers between the onMouseEnter and onMouseLeave.
Can you help me understand the problem?
My code:
import { TooltipClose } from "../../../utils/svg";
export default function CustomTooltipsRed() {
const [activeToolTip, setActiveToolTip] = useState(false);
let TooltipTimeout;
const showToolTip = () => {
TooltipTimeout = setTimeout(() => {
setActiveToolTip(true);
}, 50);
};
const hideToolTip = () => {
setActiveToolTip(false);
clearInterval(TooltipTimeout);
};
return (
<div className="flex items-center justify-center ">
<button
className="bg-pink-500 text-white font-bold uppercase text-sm px-6 py-3 rounded shadow mr-1 mb-1 "
onMouseEnter={() => showToolTip()}
onMouseLeave={() => hideToolTip()}
>
Hover button
</button>
{activeToolTip ? (
<>
<div className="justify-center items-center flex fixed inset-0 outline-none focus:outline-none ">
<div className="relative w-auto my-6 mx-auto max-w-xl">
{/*content*/}
<div className="border-0 rounded-lg shadow-lg relative flex flex-col w-full bg-[#F43F5E] outline-none">
{/*header*/}
<div className="flex items-center justify-start p-2 rounded-t">
<div className="mr-2">
<i><TooltipClose /></i>
</div>
<h3 className="text-base font-semibold text-white">
P1 - Priority Issue
</h3>
</div>
{/*body*/}
<div className="relative p-6 flex-auto bg-[#1E293B] rounded-b-lg">
<p className="text-[#E2E8F0] leading-relaxed">
We have detected that your tenant has legacy protocols enabled.
Why is this an issue? Legacy protocols can not enforce multi factor
authentication and are considered a security risk.
</p>
Read More
</div>
</div>
</div>
</div>
<div className="inset-0 z-40"></div>
</>
) : null}
</div >
);
}
Add pointer-events-none to the tooltip.

How to persist the value of state variables after window reloads in nextJS?

Intro
I am designing an e-commerce platform in nextJS. It consists of Tshirts page like this .
After clicking any of the four t-shirts, i went to that particular t-shirt page in slug.js file, where the page looks like this.
This is where I am facing issue.
Issue
As you can see that the URL of the black t-shirt page is
http://localhost:3000/products/wear-the-chess-formula-S-Black
which is correct, but after that if I select any of the color and size variant, say (Green,M), It does not get reflected in the URL after the reload . You can also see the top-left on the image below. Also after the reload I see the black t-shirt image again.
For this functionality , I had built the following function.
const refreshVariant = (newsize, newcolor) => {
let url = `http://localhost:3000/products/${colorSizeSlug[newcolor][newsize]["slug"]}`;
//console.log("url = ",url, "newcolor - ",newcolor, "newsize - ", newsize)
//console.log("newvaraint -", colorSizeSlug)
window.location = url;
};
What I thought?
I think that clicking the colour button & size dropdown option , the value of color&size changes but then after reloads, it again goes to its initial value.
Live Behaviour
what i want?
I want to retain the values of these two state variables color & size, so that every time we select the color, the same color dot get highlighted and same gets reflected in URL. For example if I select the Green color and M size, then Green color dot should get highlighted with border and this URL should come
http://localhost:3000/products/wear-the-chess-formula-M-Green
code
The file responsible for this is slug.js
import React, { useState } from "react";
import { useRouter } from "next/router";
import mongoose from "mongoose";
import Product from "../../models/Product";
//colosizeSlug :
// {
// Black: {S: {slug: "wear-the-chess-formula-1"}},
// Blue: {M: {slug: "wear-the-chess-formula-2"}},
// Green: {L: {slug: "wear-the-chess-formula-3"}},
// Red: {XL: {slug: "wear-the-chess-think-1"} ,XXL: {slug: "wear-the-chess-think-2"}}
// }
// MY LOGS
//date - 17-4-22
//issue of setColor & setSize is resolved but now the window reload functon disabled. It is beacuse when the page reloads , the state value of color and size revert back to the its initial value & not the current value. That's why you can see the updated url is not displayed in chrome tab.
const Slug = ({ addToCart, all_Tshirts, colorSizeSlug }) => {
//console.log(all_Tshirts, colorSizeSlug);
//console.log("keys = ",Object.keys(colorSizeSlug["Red"]))
const router = useRouter();
const { slug } = router.query;
const [pin, setPin] = useState();
const [isAvailable, setIsAvailable] = useState();
const [color, setColor] = useState(all_Tshirts[0].color);
const [size, setSize] = useState(all_Tshirts[0].size);
console.log("size =", size, " color = ", color);
const checkservicibilty = async () => {
let pins = await fetch("http://localhost:3000/api/pincode");
let pinjson = await pins.json();
//console.log(pin, pinjson);
if (pinjson.includes(pin)) {
setIsAvailable(true);
//console.log("Available");
} else {
setIsAvailable(false);
//console.log("NOt");
}
};
const onChangePin = (e) => {
setPin(e.target.value);
};
const refreshVariant = (newsize, newcolor) => {
let url = `http://localhost:3000/products/${colorSizeSlug[newcolor][newsize]["slug"]}`;
//console.log("url = ",url, "newcolor - ",newcolor, "newsize - ", newsize)
//console.log("newvaraint -", colorSizeSlug)
window.location = url;
};
return (
<>
<section className="text-gray-600 body-font overflow-hidden">
<div className="container px-5 py-16 mx-auto">
<div className="lg:w-4/5 mx-auto flex flex-wrap">
<img
alt="ecommerce"
className="lg:w-1/2 w-full h-80 lg:h-[36rem] object-cover object-top rounded"
src={`${colorSizeSlug[color][size]["url"]}`}
/>
<div className="lg:w-1/2 w-full lg:pl-10 lg:py-6 mt-6 lg:mt-0">
<h2 className="text-sm title-font text-gray-500 tracking-widest">
CHESS WEAR
</h2>
<h1 className="text-gray-900 text-3xl title-font font-medium mb-1">
Men&apos;s Chess Formula T-Shirt ( L-Black )
</h1>
<div className="flex mb-4">
<span className="flex items-center">
.
.
.
<span className="text-gray-600 ml-3">4 Reviews</span>
</span>
<span className="flex ml-3 pl-3 py-2 border-l-2 border-gray-200 space-x-2s">
.
.
.
</span>
</div>
<p className="leading-relaxed">
Fam locavore kickstarter distillery. Mixtape chillwave tumeric
sriracha taximy chia microdosing tilde DIY. XOXO fam indxgo
juiceramps cornhole raw denim forage brooklyn. Everyday carry +1
seitan poutine tumeric. Gastropub blue bottle austin listicle
pour-over, neutra jean shorts keytar banjo tattooed umami
cardigan.
</p>
<div className="flex mt-6 items-center pb-5 border-b-2 border-gray-100 mb-5">
<div className="flex">
<span className="mr-3">Color</span>
{Object.keys(colorSizeSlug).includes("Red") &&
Object.keys(colorSizeSlug["Red"]).includes(size) && (
<button
onClick={() => {
refreshVariant(size, "Red");
setColor("Red");
}}
className={`border-2 bg-red-600 rounded-full w-6 h-6 focus:outline-none ${
color === "Red" ? "border-black" : "border-gray-300"
}`}
></button>
)}
{Object.keys(colorSizeSlug).includes("Green") &&
Object.keys(colorSizeSlug["Green"]).includes(size) && (
<button
onClick={() => {
refreshVariant(size, "Green");
setColor("Green");
}}
className={`border-2 ml-1 bg-green-600 rounded-full w-6 h-6 focus:outline-none ${
color === "Green" ? "border-black" : "border-gray-300"
}`}
></button>
)}
{Object.keys(colorSizeSlug).includes("Black") &&
Object.keys(colorSizeSlug["Black"]).includes(size) && (
<button
onClick={() => {
refreshVariant(size, "Black");
setColor("Black");
}}
className={`border-2 ml-1 bg-black rounded-full w-6 h-6 focus:outline-none ${
color === "Black" ? "border-black" : "border-gray-300"
}`}
></button>
)}
{Object.keys(colorSizeSlug).includes("White") &&
Object.keys(colorSizeSlug["White"]).includes(size) && (
<button
onClick={() => {
refreshVariant(size, "White");
setColor("White");
}}
className={`border-2 bg-white rounded-full w-6 h-6 focus:outline-none ${
color === "White" ? "border-black" : "border-gray-300"
}`}
></button>
)}
{Object.keys(colorSizeSlug).includes("Yellow") &&
Object.keys(colorSizeSlug["Yellow"]).includes(size) && (
<button
onClick={() => {
refreshVariant(size, "Yellow");
setColor("Yellow");
}}
className={`border-2 bg-yellow-500 rounded-full w-6 h-6 focus:outline-none ${
color === "Yellow"
? "border-black"
: "border-gray-300"
}`}
></button>
)}
{Object.keys(colorSizeSlug).includes("Purple") &&
Object.keys(colorSizeSlug["Purple"]).includes(size) && (
<button
onClick={() => {
refreshVariant(size, "Purple");
setColor("Purple");
}}
className={`border-2 bg-purple-600 rounded-full w-6 h-6 focus:outline-none ${
color === "Purple"
? "border-black"
: "border-gray-300"
}`}
></button>
)}
{Object.keys(colorSizeSlug).includes("Maroon") &&
Object.keys(colorSizeSlug["Maroon"]).includes(size) && (
<button
onClick={() => {
refreshVariant(size, "Maroon");
setColor("Maroon");
}}
className={`border-2 bg-rose-700 rounded-full w-6 h-6 focus:outline-none ${
color === "Maroon"
? "border-black"
: "border-gray-300"
}`}
></button>
)}
{Object.keys(colorSizeSlug).includes("Blue") &&
Object.keys(colorSizeSlug["Blue"]).includes(size) && (
<button
onClick={() => {
refreshVariant(size, "Blue");
setColor("Blue");
}}
className={`border-2 bg-blue-500 rounded-full w-6 h-6 focus:outline-none ${
color === "Blue" ? "border-black" : "border-gray-300"
}`}
></button>
)}
</div>
<div className="flex ml-6 items-center">
<span className="mr-3">Size</span>
<div className="relative">
<select
value={size}
onChange={(e) => {
refreshVariant(e.target.value, color);
setSize(e.target.value);
}}
className="rounded border appearance-none border-gray-300 py-2 focus:outline-none focus:ring-2 focus:ring-blue-200 focus:border-blue-500 text-base pl-3 pr-10"
>
{Object.keys(colorSizeSlug[color]).includes("S") && (
<option value={"S"}>S</option>
)}
{Object.keys(colorSizeSlug[color]).includes("M") && (
<option value={"M"}>M</option>
)}
{Object.keys(colorSizeSlug[color]).includes("L") && (
<option value={"L"}>L</option>
)}
{Object.keys(colorSizeSlug[color]).includes("XL") && (
<option value={"XL"}>XL</option>
)}
{Object.keys(colorSizeSlug[color]).includes("XXL") && (
<option value={"XXL"}>XXL</option>
)}
</select>
<span className="absolute right-0 top-0 h-full w-10 text-center text-gray-600 pointer-events-none flex items-center justify-center">
<svg
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
className="w-4 h-4"
viewBox="0 0 24 24"
>
<path d="M6 9l6 6 6-6"></path>
</svg>
</span>
</div>
</div>
</div>
<div className="flex">
<span className="title-font font-medium text-2xl text-gray-900">
₹499
</span>
<button
onClick={() =>
addToCart(
slug,
1,
499,
"wear the chess(XL, Red)",
"XL",
"Red"
)
}
className="flex ml-auto md:ml-24 text-sm lg:text-base text-white bg-blue-500 border-0 py-2 px-4 lg:px-6 focus:outline-none hover:bg-blue-600 rounded"
>
Add to Cart
</button>
<button className="flex ml-1 md:ml-2 text-white text-sm lg:text-base bg-blue-500 border-0 py-2 px-4 lg:px-6 focus:outline-none hover:bg-blue-600 rounded">
Buy Now
</button>
<button className="rounded-full w-10 h-10 bg-gray-200 p-0 border-0 inline-flex items-center justify-center text-gray-500 ml-4 md:ml-6">
<svg
fill="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
className="w-5 h-5"
viewBox="0 0 24 24"
>
<path d="M20.84 4.61a5.5 5.5 0 00-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 00-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 000-7.78z"></path>
</svg>
</button>
</div>
<div className="pin mt-6 space-x-2 flex text-sm">
<input
type="text"
id="name"
name="name"
onChange={onChangePin}
placeholder="Enter Pincode Here"
className="w-3/5 md:w-2/5 bg-gray-100 bg-opacity-50 rounded border border-gray-300 focus:border-blue-500 focus:bg-white focus:ring-2 focus:ring-indigo-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
/>
<button
onClick={checkservicibilty}
className="text-white bg-blue-500 border-0 py-2 px-6 focus:outline-none hover:bg-blue-600 rounded"
>
Check
</button>
</div>
{isAvailable != null ? (
isAvailable ? (
<div className="text-sm mt-4 text-green-600">
Yay ! This pincode is serviceable .
</div>
) : (
<div className="text-sm mt-4 text-red-600">
Sorry ! We do not deliver to this pincode yet .
</div>
)
) : (
<div></div>
)}
</div>
</div>
</div>
</section>
</>
);
};
// taking all the variants of a tshirt/hoodies of same title from the slug
// for eg, if slug=wear-chess-think-2, then it will give all the 'Chess-Think Tshirts'=title tshirts in variant-array
export async function getServerSideProps(context) {
if (!mongoose.connections[0].readyState) {
await mongoose.connect(process.env.MONGO_URI);
}
let product = await Product.findOne({ slug: context.query.slug });
let all_Tshirts = await Product.find({ title: product.title });
//all tshirts of same title with the given 'slug'
//console.log("all -",all_Tshirts)
let colorSizeSlug = {};
//eg: {'Red': {'XL':'wear-the-chess-king-1}}
for (let item of all_Tshirts) {
if (Object.keys(colorSizeSlug).includes(item.color)) {
colorSizeSlug[item.color][item.size] = { slug: item.slug, url: item.img };
} else {
colorSizeSlug[item.color] = {};
colorSizeSlug[item.color][item.size] = { slug: item.slug, url: item.img };
}
}
return {
props: {
all_Tshirts: JSON.parse(JSON.stringify(all_Tshirts)),
colorSizeSlug: JSON.parse(JSON.stringify(colorSizeSlug)),
}, // will be passed to the page component as props
};
}
export default Slug;
The key here is to use router push with shallow set as true, in order to avoid page rerendering.
const refreshVariant = (newsize, newcolor) => {
const url = `http://localhost:3000/products/${colorSizeSlug[newcolor][newsize]["slug"]}`
router.push(url, undefined, { shallow: true })
}
Instead of using window.location you need to use router.push
https://nextjs.org/docs/api-reference/next/router#routerpush

Categories