dynamic modal not working properly in Next Js - javascript

I'm implementing a modal popup for a youtube in my Next app. I'm mapping through json object and passing them into cards. and when user selects specific card it displays a modal popup. and in that popup it is passed a url for a youtube vid.
my index.js code.
const Contents = ({result , index}) => {
let router = useRouter();
const [modal, setModel] = useState(false);
const [temp, setTemp] = useState([]);
const getData = (vid)=>{
let temp = [vid];
setTemp(item =>[1, ...temp]);
return setModel(true);
}
//console.log(result.items[1].snippet.title);
return (
<div className='grid md:grid-cols-2 gap-y-6 place-items-center lg:grid-cols-4 px-5 pt-5'>
{result.items && result.items.map((res, idx)=>{
//console.log(res.snippet);
return (
<div>
{/* <ContentCard result={res} index={index} key={idx}/> */}
<div>
<div onClick={()=>getData(res.snippet.resourceId?.videoId)}>
<div className='flex flex-col hover:scale-110 hover:transition-all hover:duration-200 hover:ease-in ease-out duration-200'>
<div className=' relative snap-center rounded-lg h-40 w-64 '>
<Image
src={res.snippet.thumbnails.standard?.url || res.snippet.thumbnails.medium?.url || res.snippet.thumbnails.high?.url}
layout="fill"
objectFit="cover"
className={`h-full w-full bg-cover rounded-lg bg-no-repeat`} />
</div>
<Moment className=' text-dark-gray' date={res.snippet.publishedAt} format={"MMM Do YY"}/>
<div className=' text-sm font-thin font-serif text-med-gray'>{res.snippet.title.slice(0, 25).concat('...')}</div>
</div>
</div>
</div>
{
modal === true ? <Modal vid={temp[1]} />: ''
}
</div>
)
})}
</div>
)
}
and my modal.js
return(
<div className=" fixed inset-0 bg-bg-black bg-opacity-70 backdrop-blur-sm flex justify-center items-center">
<div className="">
<Video vid={vid} />
{/* <h1>{vid}</h1> */}
</div>
</div>
)
Video.js
const opts ={
height: '70%',
wresulthth: '70%',
playerVars: {
autoplay: 1,
}
}
return (
<YouTube videoId={vid} opts={opts}/>
);
my issue is when modal is fired up it displays the prop data, but when i use it for youtube videos it doesn't work. also uses a lot of my cpu and it lags when only modal is opened. what is the fix?

Related

How to get child click on paremt in react js

I have a child and parent compoinents as follows,
When I click on the div I am not able to get the alert in parent component but when I keep button in child component I can able to get the aler. Not sure what I am doing, Can any one please help. Thanks.
Child Comp:
const PrimaryNavigation = ({ className, data, childToParent }: IPrimaryNavigation) => {
return (
<div className={`${className} w-full bg-black grid grid-cols-12 gap-4 py-12 pl-12`}>
<div className="relative col-span-2 md:col-span-1 w-5 h-5 md:w-6 md:h-6 cursor-pointer" onClick={() => { childToParent() }}>>
<Image src={searchSvg} layout="fill" objectFit="contain" alt="hamburgerIcon" />
</div>
<div className="relative col-span-2 md:col-span-1 md:w-6 md:h-6 w-5 h-5 cursor-pointer" >
<Image src={hamburgerSvg} layout="fill" objectFit="contain" alt="searchIcon" />
</div>
</div>
)
}
Parent Comp:
const Header = ({ data }: IHeader) => {
const childToParent = () => {
alert('hi')
}
return (
<div>
<header className="w-full md-0 md:md-6 bg-black grid grid-cols-12 gap-4">
<PrimaryNavigation className="col-span-11" data={data.primaryNavigationCollection.items} childToParent={childToParent} />
</header>
</div>
)
}
export default Header

problem in sending or loading data to localstorage, when I do refresh- cart data is not there to show. I am loading data from firebase

I am loading data from firebase, so when I refresh, it takes time to load it. And added data are not present in the cart that,s why I am not getting any product in the cart. But I did follow the same process to store user data, and it,s working fine, which means I am getting a registered profile in console. Can anyone suggest me a solution for getting data in the cart?
sending data:
import { motion } from "framer-motion";
import React, { useEffect, useRef, useState } from "react";
import { MdShoppingBasket } from "react-icons/md";
import { actionType } from "../context/reducer";
import { useGlobalState } from "../context/stateProvider";
import NotFound from "../img/NotFound.svg";
const RowContainer = ({ flag, data, scrollValue }) => {
const [{ cartItems }, dispatch] = useGlobalState();
const [items, setItems] = useState([]);
const rowContainer = useRef();
const addToCart = () => {
dispatch({
type: actionType.SET_CARTITEMS,
cartItems: items,
});
localStorage.setItem("cartItems", JSON.stringify(items));
};
useEffect(() => {
addToCart();
}, [items]);
useEffect(() => {
rowContainer.current.scrollLeft += scrollValue;
}, [scrollValue]);
return (
<div
ref={rowContainer}
className={`w-full my-12 flex items-center ${
flag
? "overflow-x-scroll scrollbar-none scroll-smooth"
: "overflow-x-hidden flex-wrap justify-center"
}`}
>
{data && data.length > 0 ? (
data.map((item) => {
const { id, price, imageURL, calories, title } = item;
return (
<div
key={id}
className="w-350 min-w-[300px] md:min-w-[380px] md:my-10 backdrop-blur-lg mx-1 my-2 lg:mx-2 "
>
<div className="w-full flex flex-row items-center justify-between bg-white rounded-lg drop-shadow-lg py-2 px-4 hover:bg-whiteAlpha min-h-[150px]">
<motion.img
whileHover={{ scale: 1.2 }}
src={imageURL}
alt="img"
className="w-30 max-h-40 -mt-8 duration-100 drop-shadow-2xl"
/>
<div className="flex flex-col items-end justify-end gap-4">
<motion.div
whileTap={{ scale: 0.75 }}
className="w-8 h-8 rounded-md bg-orange-500 hover:bg-orange-600 "
onClick={() => setItems([...new Set(cartItems), item])}
>
<MdShoppingBasket className="text-white text-2xl m-1" />
</motion.div>
<div className=" w-full">
<p className="text-gray-800 md:text-lg text-base text-right">
{title}
</p>
<p className="text-sm text-gray-500 text-right">
<span className="text-orange-600">$</span> {price}
</p>
</div>
</div>
</div>
</div>
);
})
) : (
<div className="w-full flex flex-col items-center justify-center">
<img src={NotFound} className="h-340" alt="" />
<p className="text-center my-2 text-xl text-red-500">No data found</p>
</div>
)}
</div>
);
};
export default RowContainer;
fetching data
export const fetchCart = () => {
const cartInfo =
localStorage.getItem("cartItems") !== "undefined"
? JSON.parse(localStorage.getItem("cartItems"))
: localStorage.clear();
return cartInfo ? cartInfo : [];
};
You are clearing your data for no reason
localStorage.getItem("cartItems") !== "undefined"
should be
localStorage.getItem("cartItems") !== undefined

Button Function Not calling From Another file in React

I need to call CutomerDashboard.js file's "toggleIsTrucated" function and "isTruncated" to CustomerNotice.js files button onClick and text change places, How can I call that?
(In this customer dashboard file I'm creating a Read function to show some extent of notice text)
import React, {useState,useEffect} from 'react';
import { Input, Row, Col, Button } from 'antd';
import {fetchDashboardMetrics} from "./DashboardApi";
import {items} from "./DashboardItems";
import axios from 'axios';
import CustomerNotice from "./CustomerNotice";
function Read ({children}) {
const text = children;
const [isTruncated, setIsTrucated] = useState(true);
const result = isTruncated ? text.slice(0,90) : text;
function toggleIsTrucated(){
setIsTrucated(!isTruncated);
}
return (
<div>
{result}....
</div>
);
}
const CustomerDashboard = () => {
const [features, setFeatures] = useState(items);
const source = axios.CancelToken.source()
const [notice, setNotice] = useState(<Read>Customer Notice: Optimism Is Invaluable For The Meaningful Life. With A Firm Belief In A Positive Future You Can Throw Yourself Into The Service Of That Which Is Larger Than You Are. -Martin Seligman-</Read>);
const [noticeVisibility, setNoticeVisibility] = useState(true);
useEffect(() => {
fetchDashboardMetrics(features, setFeatures,source.token)
return (() => {
source.cancel();
})
}, []);
return (
<>
<div className='md:pl-8 sm:pl-0'>
<div className='my-5 '>
<p className='mb-8'>My Account - Dashboard Overview</p>
{noticeVisibility && <CustomerNotice notice={notice} setNoticeVisibility={setNoticeVisibility}/>}
</div>
<ul role="list" className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{features.map((feature) => (
<li key={feature.name} className="col-span-1 bg-white rounded-lg shadow divide-y divide-gray-200 relative">
<div className="w-full flex items-center justify-between p-6 space-x-6">
<div className="flex-1 truncate">
<div className="flex items-center space-x-3 justify-between">
<h3 className="text-gray-900 text-lg truncate">{feature.name}</h3>
{feature.isNew && (
<div className="absolute -top-2 -right-2 p-1 px-4 text-white text-sm bg-red-500">
New
</div>
)}
</div>
</div>
</div>
<div>
<div className={'mx-4 mt-2 mb-3 '}>
{feature.details.map((singleDetail) => {
return (
<div className={'flex justify-between text-base'}>
<span>{singleDetail.name}</span>
<span>{singleDetail.value}</span>
</div>
)
})}
</div>
</div>
</li>
))}
</ul>
</div>
</>
)
}
export default CustomerDashboard;
import React, {useState,useEffect} from 'react';
import {XIcon} from "#heroicons/react/solid";
const CustomerNotice = ({notice, setNoticeVisibility}) => {
return (
<div>
<div className="mt-8 pb-2 sm:pb-5">
<div className="max-w-7xl mx-auto px-2 sm:px-6 lg:px-8">
<div className="p-2 rounded-lg bg-orange-600 shadow-lg sm:p-3">
<div className="flex items-center justify-between flex-wrap">
<div className="w-0 flex-1 flex items-center">
<p className="ml-3 font-medium text-white truncate">
<span className="md:inline">{notice}</span>
</p>
</div>
<div className="order-3 mt-2 flex-shrink-0 w-full sm:order-2 sm:mt-0 sm:w-auto">
<a
href="#"
className="flex items-center justify-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-orange-600 bg-white hover:bg-orange-50"
>
<button onClick={toggleIsTrucated}>{isTruncated ? "Read More" : "Read Less"}</button>
</a>
</div>
<div className="order-2 flex-shrink-0 sm:order-3 sm:ml-2">
<button
onClick={() => setNoticeVisibility(false)}
type="button"
className="-mr-1 flex p-2 rounded-md hover:bg-orange-500 focus:outline-none focus:ring-2 focus:ring-white"
>
<span className="sr-only">Dismiss</span>
<XIcon className="h-6 w-6 text-white" aria-hidden="true"/>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default CustomerNotice;
If this is not possible please suggest me a possible way.
Instead of doing a bunch of hacks, I would recommend simplifying the structure of your components.
import { useState } from 'react'
export default function CustomerDashboard() {
// I am not sure why you want to keep notice in state,
// because in your example you did not call setNotice
const [notice, setNotice] = useState(`
Customer Notice: Optimism Is Invaluable For The Meaningful Life.
With A Firm Belief In A Positive Future You Can Throw Yourself Into The Service
Of That Which Is Larger Than You Are. -Martin Seligman
`)
const [isNoticeVisible, setIsNoticeVisible] = useState(true)
return (
<div>
<h1>My Account - Dashboard Overview</h1>
{isNoticeVisible && (
<CustomerNotice
notice={notice}
setIsNoticeVisible={setIsNoticeVisible}
/>
)}
</div>
)
}
function CustomerNotice(props) {
const { notice, setIsNoticeVisible } = props
const [isTruncated, setIsTruncated] = useState(true)
function toggleIsTruncated() {
setIsTruncated(!isTruncated)
}
return (
<div>
<Read text={notice} isTruncated={isTruncated} />
<button onClick={toggleIsTruncated}>
{isTruncated ? 'Read More' : 'Read Less'}
</button>
<button onClick={() => setIsNoticeVisible(false)}>Dismiss</button>
</div>
)
}
function Read(props) {
const { text, isTruncated } = props
const result = isTruncated ? text.slice(0, 90) : text
return <div>{result}....</div>
}
List of the things that were bad in your code.
Keeping the component instance in the state. It is hard to manage. Even your simple case proves that.
Keeping the toggleIsTruncated function inside the Read component. I think we should keep it outside and pass only 2 props to the Read component. I enable exposed only two things
const { text, isTruncated } = props
As you can see it is easy to maintain and allow us to do whatever we want.
PS. If my review and example were helpful please leave the thumbs up.

|React:useOutsideClick hook gives forwardRef warning message

From firebase I fetch data and map this data to be shown in cards, every card has edit component and CardComponent(edit component parent) which use ref provided from useHandleOpen custom hook
Error message:
Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
Component where ref is in use
export default function EditCard(id) {
const { ref, isOpen, setIsOpen } = useHandleOpen(false);
return (
<div>
<GoKebabVertical
ref={ref}
className="cursor-pointer "
onClick={() => setIsOpen(true)}
/>
{isOpen && (
<div
ref={ref}
className="w-20 h-15 bg-white z-30 rounded-md absolute text-center
top-0 right-0
"
>
<p className="hover:bg-blue-300 hover:text-white cursor-pointer">
Edytuj
</p>
<p
className="bg-red-400 text-white font-semibold hover:bg-red-600 cursor-pointer"
onClick={() => {}}
>
UsuĊ„
</p>
</div>
)}
</div>
);
}
export const Edit = memo(EditCard);
Card component which use ref tho and its Edit parent
const Card = ({ data, id }) => {
const editRef = useRef(null);
const { ref, isOpen, setIsOpen } = useHandleOpen(editRef);
return (
<div
className="bg-gradient-to-r from-blue-200 to-purple-500
w-64 h-28 rounded-md relative
"
>
<div className="relative top-0 left-0">
<p className="px-2 pt-1 font-bold text-gray-800 text-lg">
{data.subject_name}
</p>
<p className="px-2 text-xs text-gray-800">Sala 101</p>
</div>
<div
className="absolute top-0 right-0 rounded-r-md
rounded-b-none
bg-white w-6 h-7
grid place-items-center
"
>
<Edit id={id} />
</div>
<div className="absolute bottom-9 left-0 mb-2.5">
<p className="px-2 text-xs text-gray-800">{data.teacher}</p>
</div>
<div className=" flex direction-row mt-7 text-center w-full justify-end align-bottom pr-2 ">
<div className="rounded-lg w-14 h-7 mx-3 bg-purple-300">
<a
href={data.link_subject}
className="text-gray-800
"
target="_blank"
rel="noreferrer"
>
Lekcja
</a>
</div>
<div className="rounded-lg w-14 h-7 bg-gray-800 ">
<a
href={data.link_online_lesson}
target="_blank"
rel="noreferrer"
className="text-white"
>
Online
</a>
</div>
</div>
<div
className=" absolute bottom-0 left-0 rounded-l-md
bg-white w-7 h-6 grid place-items-center devide-solid"
>
{isOpen ? (
<AiOutlineUp
className="cursor-pointer"
ref={ref}
onClick={() => setIsOpen(true)}
/>
) : (
<AiOutlineDown
className="cursor-pointer"
ref={ref}
onClick={() => setIsOpen(true)}
/>
)}
</div>
{isOpen && (
<div
className="bg-gradient-to-r from-blue-200 to-purple-500 w-full text-left rounded-b-md p-4 "
ref={ref}
>
<p className="font-bold text-gray-800 text-sm ">Lekcje w:</p>
<p className="text-gray-800 text-sm">PN: 8-12</p>
</div>
)}
</div>
);
};
export const CardSubject = memo(Card);
Custom hook with ref:
export default function useHandleOpen() {
const [isOpen, setIsOpen] = useState(false);
const ref = useRef(null);
const handleClickOutside = (event) => {
if (ref.current && !ref.current.contains(event.target)) {
setIsOpen(!isOpen);
}
};
useEffect(() => {
document.addEventListener("click", handleClickOutside, !isOpen);
return () => {
document.removeEventListener("click", handleClickOutside, !isOpen);
};
});
return { ref, isOpen, setIsOpen };
}
Edit: Tried change it this way, but this displays warning too.
export default function useHandleOpen(ref) {
const [isOpen, setIsOpen] = useState(false);
const handleClickOutside = (event) => {
if (ref.current && !ref.current.contains(event.target)) {
setIsOpen(!isOpen);
}
};
useEffect(() => {
document.addEventListener("click", handleClickOutside, !isOpen);
return () => {
document.removeEventListener("click", handleClickOutside, !isOpen);
};
});
return { ref, isOpen, setIsOpen };
}
And use hook like this:
const editRef = useRef(null);
const { ref, isOpen, setIsOpen } = useHandleOpen(editRef);
Ok i fixed it by removing ref from places where i use onClick method for changing the state

How to render components with individual keys?

I have a dashboard and I want to use React-Grid-Layout but I render the components only if they have been favorited. To use the gird layout each div needs a key="a" or key="b" depending on the layout defined each key needs to be different.
How can I go about giving each div an individual key? When it renders on-screen in its current form it renders two divs with the same cards in where I need it to render one div for each card.
const layout = [
{ i: "a", x: 0, y: 0, w: 1, h: 2 },
{ i: "b", x: 1, y: 0, w: 3, h: 2, minW: 2, maxW: 4 },
{ i: "c", x: 4, y: 0, w: 1, h: 2 },
];
render function
const userHasFavorite = (data, favIds, layout) => {
if (favIds && data) {
const filteredData = data.filter((idsArr) =>
favIds.split(",").includes(idsArr.id)
);
const keysMapped = layout.map((ids) => ids.i);
console.log(keysMapped);
return (
<div key={keysMapped}>
<PriceCard data={filteredData} />
</div>
);
} else {
return <p1>No Cards Have Been Faved</p1>;
}
};
Grid Layout
<GridLayout
className="layout"
layout={layout}
cols={12}
rowHeight={30}
width={1200}
>
{isLoading ? (
<LoadingCard />
) : (
userHasFavorite(data, favoritedIds, layout)
)}
</GridLayout>
PRICE CARD
return (
<dl className="mt-5 ml-5 grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
{data.map((item) => (
<div
key={item.id}
className="flex-row bg-white hover:bg-gray-100 dark:hover:bg-purple-700 dark:bg-secondaryDark h-24 pt-2 px-4 pb-12 sm:pt-6 sm:px-6 shadow-2xl rounded-lg overflow-hidden"
>
<div className="flex h-2 ">
{/* TODO: ADD TRANSISITON */}
{cardFaved.some((cardFaved) => cardFaved.id === item.id) ? (
<MinusSmIcon
onClick={() => removeCardFavClick(item.id)}
className="ml-auto h-5 w-5 cursor-pointer fill-current text-red-600 "
/>
) : (
<PlusSmIcon
onClick={() => handleCardFavClick(item.id)}
className="ml-auto h-5 w-5 cursor-pointer fill-current text-green-600 "
/>
)}
</div>
<dt>
<div className="absolute rounded-md p-3">
<img className="h-6 w-6 mr-3" src={item.image} />
</div>
<p className="ml-16 text-sm pb-0 font-medium text-gray-500 dark:text-white truncate">
{item.name.toUpperCase()}
</p>
</dt>
<dd className="ml-16 pb-7 flex items-baseline sm:pb-7">
<p className="text-2xl font-semibold text-gray-900 dark:text-white">
{formatDollar(item.current_price)}
</p>
</dd>
</div>
))}
</dl>
);
};
export default PriceCard;
Error I'm having is the cards render 2 in one div
Error 2
Ok, so from the React-Grid-Layout official usage, You need to map() directly within the <GridLayout/> component.
So here a good suggestion:
Within the GridLayout:
WITH map()
<GridLayout className="layout" layout={layout} cols={12} rowHeight={30} width={1200}>
{isLoading
? <LoadingCard />
: layout.map(({ i }) =>
<div key={i}>
// change this from a typical function into a React component
<UserHasFavorite data={data} favIds={favoritedIds} />
</div>
)}
</GridLayout>
WITHOUT map()
<GridLayout className="layout" layout={layout} cols={12} rowHeight={30} width={1200}>
{isLoading
? <LoadingCard />
: <React.Fragment>
<div key="a"><UserHasFavorite data={data} favIds={favoritedIds} /></div>
<div key="b"><UserHasFavorite data={data} favIds={favoritedIds} /></div>
<div key="c"><UserHasFavorite data={data} favIds={favoritedIds} /></div>
</React.Fragment>}
</GridLayout>
Within the child component
const UserHasFavorite = (data, favIds) => {
if (favIds && data) {
const filteredData = data.filter((idsArr) =>
favIds.split(",").includes(idsArr.id)
);
// <div> is moved up into the GridLayout
return <PriceCard data={filteredData} />;
} else {
return <p1>No Cards Have Been Faved</p1>;
}
};
UPDATE: To handle issue of rendering multiple "similar" cards
Within the PriceCard, there is this...
return (
<dl className="mt-5 ml-5 grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
{data.map((item) => (
<div
key={item.id}
className="flex-row bg-white hover:bg-gray-100 dark:hover:bg-purple-700 dark:bg-secondaryDark h-24 pt-2 px-4 pb-12 sm:pt-6 sm:px-6 shadow-2xl rounded-lg overflow-hidden"
>
</div>
)}
</dl>
)
You are mapping to display all the cards in a row. So if you updated the className="flex-column..." those two cards should render in a Column but in one <div>.
And then if you do that, you have two options:
Either you delete the <GridLayout/> and render directly like this...
// <GridLayout className="layout" layout={layout} cols={12} rowHeight={30} width={1200}>
return (
isLoading
? <LoadingCard />
: <UserHasFavorite data={data} favIds={favoritedIds} />
)
// </GridLayout>
By removing the <GridLayout/> which is used for display, you will have just two cards rendered in a column (handled with PriceCard component).
2.Or render the <PriceCard/> directly to within the like this...
// Parent component -> GridLayout
export const ParentComponent = props => {
// I'm not sure where "data" and "favoritedIds" are coming from yet,
// but we check like "data &&..." to ensure they are defined
// before we attempt to filter
const filteredData = data && data.filter(
(idsArr) => favoritedIds && favoritedIds.split(",").includes(idsArr.id)
);
return (
<GridLayout className="layout" layout={layout} cols={12} rowHeight={30} width={1200}>
{isLoading
? <LoadingCard />
: (!!data && !!filteredData)
? <React.Fragment>
<div key="a"><PriceCard data={filteredData[0]} /></div>
<div key="b"><PriceCard data={filteredData[1]} /></div>
<div key="c"><PriceCard data={filteredData[2]} /></div>
</React.Fragment>}
: <p1>No Cards Have Been Faved</p1>
</GridLayout>
)
}
// Child component -> PriceCard
export const ChildComponent = props => {
return (
<dl className="mt-5 ml-5 grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
{data && /* don't map here -> we moved it to parent */
<div className="flex-row bg-white hover:bg-gray-100 dark:hover:bg-purple-700 dark:bg-secondaryDark h-24 pt-2 px-4 pb-12 sm:pt-6 sm:px-6 shadow-2xl rounded-lg overflow-hidden">
/* make necessary changes */
</div>}
</dl>
)
}
From what I read I think that you need to iterate and assign it to the div. You can do something like this.
return (
{filteredData.map(data => (
<div key={data.i}>
<PriceCard data={filteredData} />
</div>
)
);

Categories

Resources