Reactjs setInterval with scroll only trigger once - reactjs

I have auto scroll function and scroll will start when user click the function as follow.
It is scroll 50px to y axis once when user click play button. But it is only scroll once even thought I have added interval. Interval is working because I saw the "scrolling" console.log is increasing. But scroll is not scrolling again.
May I know why scroll is not move again?
import React, { useState, useEffect, useRef } from "react";
import { useParams, NavLink } from "react-router-dom";
import { useQuery } from "#apollo/client";
import { getTrack, getTrackVariable } from "../../gql/track";
import ChordSheetJS from "chordsheetjs";
import {
YoutubeIcon,
FacebookIcon,
PlayIcon,
PauseIcon,
} from "../../assets/icons/svg_icons";
import { SettingIcon } from "../../assets/icons/svg_icons";
import paths from "../../routes/paths";
import { FacebookShareButton } from "react-share";
import GoTop from "../../components/go_top";
const TrackPage = () => {
const intervalId = useRef(null);
const { trackId } = useParams();
const [track, setTrack] = useState();
const [collapse, setCollapse] = useState(true);
const [play, setPlay] = useState(false);
const [speed, setSpeed] = useState(1);
const { loading, error, data } = useQuery(getTrack, {
variables: getTrackVariable(trackId),
});
const trackRef = useRef();
useEffect(() => {
if (!loading && !error) {
setTrack(data?.track);
}
}, [loading, error, data]);
const getChordSheet = (value) => {
const parser = new ChordSheetJS.ChordProParser();
const song = parser.parse(value);
const formatter = new ChordSheetJS.HtmlTableFormatter();
const chordSheet = formatter.format(song);
return chordSheet;
};
const handleError = (e) => {
e.target.onerror = null;
e.target.src = Monk;
};
const handleMenuCollapse = (e) => {
e.preventDefault();
setCollapse(!collapse);
};
const handleSpeedUp = () => {
setSpeed(speed + 1);
};
const handleSpeedDown = () => {
setSpeed(speed - 1);
};
const handleScroll = () => {
setPlay(!play);
if (play) {
console.log("stop");
clearInterval(intervalId.current);
} else {
let delayInMs = 100;
const onScrollStep = () => {
document.getElementById("content").scroll(0,50);
console.log("srolling")
};
intervalId.current = setInterval(onScrollStep, delayInMs);
console.log("play");
}
};
return (
<>
<div id="setting">
{/** the big div */}
<div
className={` w-36 h-56 bg-primary absolute top-[calc((100vh-384px)/2)] ${
collapse ? "hidden" : "right-0"
} " bg-primary rounded-b-lg items-center justify-center`}
>
<div>
<div className="items-center justify-center mt-5">
<div className="flex text-xs items-center justify-center ">
<span className=" text-sm text-white">Scroll</span>
</div>
<div className="flex text-xs pt-0 mt-0 items-center justify-center ">
<button
className="px-2 btn-sm flex w-20 items-center bg-transparent hover:bg-accent border text-white font-semibold hover:text-white border-white hover:border-transparent rounded "
onClick={handleScroll}
>
{play ? (
<PauseIcon className="text-white mr-2" />
) : (
<PlayIcon className="text-white mr-2" />
)}
{play ? <span>Pause</span> : <span>Play</span>}
</button>
</div>
<div className="flex text-xs items-center justify-center mt-2">
<button
className="w-auto bg-transparent mr-2 hover:bg-accent text-white font-semibold hover:text-white py-1 px-2 border border-white hover:border-transparent rounded"
onClick={handleSpeedDown}
>
-1
</button>
<button
className="w-auto bg-transparent ml-2 hover:bg-accent text-white font-semibold hover:text-white py-1 px-2 border border-white hover:border-transparent rounded"
onClick={handleSpeedUp}
>
+1
</button>
</div>
</div>
</div>
</div>
{/** the icon div */}
<div
className={`flex w-12 absolute top-[calc((100vh-384px)/2)] h-12 bg-primary
${collapse ? "animate-pulse right-0" : "right-36"}
cursor-pointer bg-primary rounded-l-lg items-center justify-center`}
onClick={handleMenuCollapse}
>
{/* <div className="w-5 h-5 bg-white rounded-full " /> */}
<SettingIcon />
</div>
</div>
<div id="track" ref={trackRef}>
<div className="flex flex-col w-full py-1 my-1 items-center bg-gray-50">
<div className="relative my-6 mx-auto md:min-w-[60%] max-h-full">
{track ? (
<div className="w-full">
<pre
className="px-5 textarea"
dangerouslySetInnerHTML={{
__html: getChordSheet(track.lyric),
}}
/>
</div>
) : (
<div></div>
)}
</div>
</div>
</div>
</>
);
};
export default TrackPage;
app.jsx
import React, { useState, useEffect } from "react";
import Header from "./components/header";
import SideMenu from "./components/side_menu";
import AppRoutes from "./routes";
import withUser from "./hocs/with_user";
import { isMobile } from "react-device-detect";
import { useLocation } from "react-router-dom";
import { AuthProvider, setAccessToken } from "./auth/auth_provider";
import { Toaster } from "react-hot-toast";
import AppContext from "./components/app_context";
import "./i18n";
import "./App.css";
function App(props) {
const [collapse, setCollapse] = useState(isMobile);
const [sideBarFull] = useState(true);
const location = useLocation();
const IsNormalPage = () => {
const blankPages = ["/login"];
for (let i = 0; i < blankPages.length; i++) {
if (location.pathname.startsWith(blankPages[i])) return
false;
}
return true;
};
useEffect(() => {
if (props.user) setAccessToken(props.user.t);
}, []);
const PageHeader = () => {
return (
<div className="h-[72px] w-full flex items-center align-middle justify-center bg-neutral shadow">
<div className="w-full text-center">
<Header />
</div>
</div>
);
};
return (
<AuthProvider user={props.user}>
<AppContext.Provider
value={{
collapse: collapse,
setCollapse: setCollapse,
}}
>
<div className="relative w-full min-h-screen h-full">
<div className="flex flex-row min-h-screen">
<div className="w-auto z-0 ">
<div className="flex-1 w-full max-h-screen mx-auto text-lg h-full shadow-lg bg-white overflow-y-auto">
{IsNormalPage() && <SideMenu showFullMenu={sideBarFull} />}
</div>
</div>
<div className="w-full max-h-screen flex flex-col z-10">
{IsNormalPage() && <PageHeader />}
<div id="content" className="flex-1 w-full max-h-screen mx-auto text-lg h-full shadow-lg bg-white overflow-y-auto">
<Toaster />
<AppRoutes />
</div>
</div>
</div>
</div>
</AppContext.Provider>
</AuthProvider>
);
}
export default withUser(App);

I think because you are toggling the play state in your component
setPlay(!play);
Are you trying to scroll to a specific div or just scroll for 50 px in the direction of y-axis? there are two approaches, you can use window or Refs.
an example using the refs to scroll to a specific node in the dom
const ScrollDemo = () => {
const myRef = useRef(null)
const executeScroll = () => { myRef.current.scrollIntoView()}
return (
<div>
<div ref={myRef}>Element to scroll to</div>
<button onClick={executeScroll}> Click to scroll </button>
<div/>
)
}
or if you just want to just scroll 50 pixel in the direction of y-axis
const scrollToTop = () => {
window.scrollTo(0,50);
};
return (
<button onClick={scrollToTop}>
Go down 50 px!
</button>
);

window.scrollTo is only working with html body. document.getElementById is only working overflow div.
useEffect(() => {
if (play) {
const onScrollStep = () => {
var e = document.getElementById("content");
if (e.scrollHeight - e.scrollTop === e.clientHeight) {
clearInterval(intervalId.current);
setPlay(!play);
return;
}
e.scroll(0, e.scrollTop + speed);
};
intervalId.current = setInterval(onScrollStep, delayInMs);
} else {
clearInterval(intervalId.current);
}
},[play, speed])

Related

state does not update using useContext even if works in another app

Im trying to build an application where I am using useState and then passing it using context. But whenever I try onCLick increment it doesnot work. state update does not happen.
Here is the state fucntion code,
import { createContext, useReducer, useState } from "react";
import { DOG_ACTION } from "../utilities/app-utilities";
import { dogList } from "../utilities/dogList";
export const DogContext = createContext();
export const DogProvider = ({ children }) => {
const [dogs, setDogs] = useState(dogList);
const dogActions = (action, payload) => {
switch (action) {
case DOG_ACTION.ADD_DOG: {
setDogs((value) => {
value.map((item) => {
if (item.id == payload) item.amount += 1;
});
return value;
});
return 0;
}
case DOG_ACTION.MINUS_DOG: {
setDogs((prev) => {
return prev.map((item) => {
if (item.id == payload) {
item.amount -= 1;
console.log(item.amount);
}
return item;
});
});
console.log("MINUS");
return 0;
}
default:
return 0;
}
};
return (
<DogContext.Provider value={{ dogs, dogActions }}>
{children}
</DogContext.Provider>
);
};
Here is the App,js,
import { LoggedInProvider } from "./hooks/logged-in-hook";
import { OverLayProvider } from "./hooks/overlay-hook";
import { Hero } from "./pages/Hero";
import { DogProvider } from "./hooks/dog-hook";
function App() {
// console.log("App Level- DIST");
return (
<DogProvider>
<LoggedInProvider>
<OverLayProvider>
<div className="App">
<Hero />
</div>
</OverLayProvider>
</LoggedInProvider>
</DogProvider>
);
}
export default App;
And lastly here is the element where I need to use onCLick,
import React, { useContext } from "react";
import { DogContext } from "../hooks/dog-hook";
import { DOG_ACTION } from "../utilities/app-utilities";
export const DogCard = ({ name, breed, speciality, amount, image, id }) => {
const action = useContext(DogContext).dogActions;
return (
<div className="flex flex-col items-start rounded-lg bg-slate-900 p-6 shadow-xl">
<div className="flex w-full flex-row items-center justify-between">
<button
onClick={() => {
action(DOG_ACTION.MINUS_DOG, id);
}}
className="flex flex-row items-center space-x-2 rounded-full bg-slate-700 px-4 py-2 text-white hover:bg-slate-800">
<img
src="https://cdn-icons-png.flaticon.com/512/9513/9513736.png"
alt="paw"
className="w-4"
/>
<p className="text-sm">Add to Cart</p>
</button>
<div className="rounded-xl bg-slate-700 px-4 py-2 text-sm text-white">
🛒:<span className="text-orange-500"> {amount}</span>
</div>
</div>
<div className="mt-8 flex w-full flex-row space-x-10">
<div>
<img
src={require(`./../images/dog-images/${image}.jpg`)}
alt="sample"
className="h-fit w-32 rounded-l-lg"
/>
</div>
<div className="flex flex-col text-white">
<h3 className="pb-8 text-3xl font-bold text-slate-200">{name}</h3>
<p>
<span className="text-gray-400">Breed:</span> {breed}
</p>
<p>
<span className="text-gray-400">Origin:</span> {speciality}
</p>
</div>
</div>
</div>
);
};
It works when I created a dummy array to taste in some other app. But not with this one. Even though everything is similar.

How do i fix the error when updating state

In my application, I am making a request to the backend with redux-thunk and fetching the pricingData. I keep the items in the pricingData first 10 items in the items variable by passing certain operations. I want to update the questionRange variable with the first items[0] value. But using setQuestionRange immediately causes infinite loop.
So i tried these:
if i have an array with multiple elements, update the setQuestionRange. But but the error still persisted
React limits the number of renders to prevent an infinite loop.
if (items.length > 0) {
setQuestionRange(items[0])
}
I used useEffect. But this caused another error:
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
useEffect(() => {
if (items) {
setQuestionRange(items[0])
}
}, [items])
How can i solve this? Here is my component:
import { Container } from '#/components/Container'
import Filters from './Filters'
import Cost from './Filters/Cost'
import { useState, useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { getAllPricing } from '#/store/actions/pricingAction'
function SwirlyDoodle({ className }) {
return (
<svg
aria-hidden="true"
viewBox="0 0 281 40"
className={className}
preserveAspectRatio="none"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M240.172 22.994c-8.007 1.246-15.477 2.23-31.26 4.114-18.506 2.21-26.323 2.977-34.487 3.386-2.971.149-3.727.324-6.566 1.523-15.124 6.388-43.775 9.404-69.425 7.31-26.207-2.14-50.986-7.103-78-15.624C10.912 20.7.988 16.143.734 14.657c-.066-.381.043-.344 1.324.456 10.423 6.506 49.649 16.322 77.8 19.468 23.708 2.65 38.249 2.95 55.821 1.156 9.407-.962 24.451-3.773 25.101-4.692.074-.104.053-.155-.058-.135-1.062.195-13.863-.271-18.848-.687-16.681-1.389-28.722-4.345-38.142-9.364-15.294-8.15-7.298-19.232 14.802-20.514 16.095-.934 32.793 1.517 47.423 6.96 13.524 5.033 17.942 12.326 11.463 18.922l-.859.874.697-.006c2.681-.026 15.304-1.302 29.208-2.953 25.845-3.07 35.659-4.519 54.027-7.978 9.863-1.858 11.021-2.048 13.055-2.145a61.901 61.901 0 0 0 4.506-.417c1.891-.259 2.151-.267 1.543-.047-.402.145-2.33.913-4.285 1.707-4.635 1.882-5.202 2.07-8.736 2.903-3.414.805-19.773 3.797-26.404 4.829Zm40.321-9.93c.1-.066.231-.085.29-.041.059.043-.024.096-.183.119-.177.024-.219-.007-.107-.079ZM172.299 26.22c9.364-6.058 5.161-12.039-12.304-17.51-11.656-3.653-23.145-5.47-35.243-5.576-22.552-.198-33.577 7.462-21.321 14.814 12.012 7.205 32.994 10.557 61.531 9.831 4.563-.116 5.372-.288 7.337-1.559Z"
/>
</svg>
)
}
export function Pricing() {
const [fieldSelectionMultipliers, setFieldSelectionMultipliers] = useState(0)
const [questionRange, setQuestionRange] = useState(0)
const [audienceCount, setAudienceCount] = useState(100)
const [totalCost, setTotalCost] = useState(null)
const dispatch = useDispatch()
const { pricingData } = useSelector((state) => state.pricing.getPricingData)
const isPricingDataEmpty = Object.keys(pricingData).length === 0
const fieldsData =
!isPricingDataEmpty &&
pricingData.items.filter((item) => item.content_object.content_type === 27)
const comboboxData =
!isPricingDataEmpty &&
pricingData.items.filter((data) => data.content_object.content_type === 41)
let items = !comboboxData
? []
: comboboxData.map((item) => ({
id: item.content_object.nanoid,
name: item.content_object.name,
multiplier: item.multiplier,
}))
const selectedFields = JSON.parse(JSON.stringify(fieldsData))
const exchangeRate = !isPricingDataEmpty && +pricingData.exchange_rate
const basePrice = !isPricingDataEmpty && +pricingData.base_price
const calculateCost = () => {
let numberFormat = Intl.NumberFormat('en-US')
let rate = exchangeRate * basePrice
let questionRangeMultiplier = questionRange ? questionRange.multiplier : 0
let total_multiplier =
(+fieldSelectionMultipliers + +questionRangeMultiplier) * rate
let total = Math.round(total_multiplier * audienceCount * 100) / 100
setTotalCost(numberFormat.format(total))
}
useEffect(() => {
calculateCost()
}, [fieldSelectionMultipliers, questionRange, exchangeRate, audienceCount])
useEffect(() => {
dispatch(getAllPricing())
}, [])
// if (items) {
// setQuestionRange(items[0])
// }
// useEffect(() => {
// if (items) {
// setQuestionRange(items[0])
// }
// }, [items])
return (
<section
id="pricing"
aria-label="Pricing"
className="bg-slate-900 py-20 sm:py-32"
>
<Container>
<div className="bg-slate-900">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h2 className="font-display text-3xl tracking-tight text-white sm:text-4xl">
<span className="relative whitespace-nowrap">
<SwirlyDoodle className="absolute top-1/2 left-0 h-[1em] w-full fill-indigo-400" />
<span className="relative">Net fiyatlama,</span>
</span>{' '}
sometext
</h2>
<p className="my-5 text-lg text-slate-400">
sometext
</p>
</div>
</div>
<div className="relative">
<div className="absolute inset-0 h-1/2 bg-slate-900" />
<div className="relative mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="mx-auto max-w-lg overflow-hidden rounded-lg shadow-lg lg:flex lg:max-w-none">
<div className="flex-1 bg-white px-6 py-8 lg:p-12">
<h3 className="text-2xl font-bold text-gray-900 sm:text-3xl sm:tracking-tight">
sometext
</h3>
<div className="mt-8">
<div className="flex items-center">
<h4 className="flex-shrink-0 bg-white pr-4 text-base font-semibold text-indigo-600">
sometext
</h4>
<div className="flex-1 border-t-2 border-gray-200" />
</div>
{selectedFields && (
<Filters
selectedFields={selectedFields}
fieldSelectionMultipliers={fieldSelectionMultipliers}
setFieldSelectionMultipliers={
setFieldSelectionMultipliers
}
/>
)}
</div>
</div>
{comboboxData && (
<Cost
items={items}
questionRange={questionRange}
setQuestionRange={setQuestionRange}
audienceCount={audienceCount}
setAudienceCount={setAudienceCount}
totalCost={totalCost}
/>
)}
</div>
</div>
</div>
</div>
</Container>
</section>
)
}

with react DND only one element accepts the drag reference

with react DND only one element accepts the drag reference i fetch data with firebase and put them in an array then map through the items and generate a card but only the last element accepts the drag ref
this is the place where I'm trying to implement it
import { useDrag } from "react-dnd";
const db = getFirestore(app);
export default function Projects({ currentUid }) {
const [projects, setProjects] = useState([]);
const [doneProjects, setDoneProjects] = useState([]);
const [tasks, setTasks] = useState([]);
const [dropDown, setDropDown] = useState(false);
const [key, setKey] = useState();
const [documentId, setDocumentId] = useState();
const [progressPercentage, setProgressPercentage] = useState(0);
const [{ isDragging }, dragRef] = useDrag(
() => ({
type: itemTypes.CARD,
item: { type: itemTypes.CARD },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
})
}),
[]
)
useEffect(() => {
// amay xwarawa bo aw projecta naya ka tawaw bwn
// const docRef3 = query(collection(db, "assignedProjects", currentUid, "projects"),where("isDone", "==", true));
const getProjects = async () => {
const docRef3 = query(
collection(db, "assignedProjects", currentUid, "projects"),
where("isDone", "==", true)
);
const docDoneSnap = await getDocs(docRef3);
onSnapshot(docRef3, (docDoneSnap) => {
const projectsDoneArr = [];
docDoneSnap.forEach((doc) => {
projectsDoneArr.push(doc.data());
});
setDoneProjects(projectsDoneArr);
});
const docRef = query(
collection(db, "assignedProjects", currentUid, "projects"),
where("isDone", "==", false)
);
// const docSnap = await getDocs(docRef);
// const projectsArr = [];
// docSnap.forEach((doc) => {
// projectsArr.push(
// {
// projects:doc.data(),
// projectId:doc.id
// }
// );
// documentId = doc.id;
// // console.log(doc.data());
// });
onSnapshot(docRef, (docSnap) => {
const projectsArr = [];
docSnap.forEach((doc) => {
projectsArr.push(
{
projects:doc.data(),
projectId:doc.id
}
);
setDocumentId(doc.id);
// console.log(doc.data());
});
setProjects(projectsArr);
});
console.log(documentId)
const taskRef = query(
collection(db, "assignedProjects", currentUid, "tasks"),
where("projectId", "==", documentId)
);
const taskSnap = await getDocs(taskRef);
const tasksArr = taskSnap.docs.map((doc) => doc.data());
setTasks(tasksArr);
};
getProjects();
}, []);
console.log(tasks)
useEffect(() => {
const getProgressPercentage = () => {
if (projects.length === 0) {
setProgressPercentage(100);
return;
}
const totalProjects = projects.length;
const totalDoneProjects = doneProjects.length;
const totalTasks = tasks.length;
const totalDoneTasks = tasks.filter((task) => task.isDone === true)
.length;
const totalProgress = totalDoneProjects + totalDoneTasks;
const total = totalProjects + totalTasks;
const percentage = Math.round((totalProgress / total) * 100);
setProgressPercentage(percentage);
};
getProgressPercentage();
}, [projects, doneProjects, tasks]);
function markProjectDone(project) {
const projectRef = collection(
db,
"assignedProjects",
currentUid,
"projects"
);
const docRef = doc(projectRef, project.projectId);
updateDoc(docRef, {
isDone: true,
});
}
const tasksToShow=tasks.map((task) => (
<div className={""}>
<p className="h-fit px-10 my-2 py-2 bg-slate-200 dark:bg-[#252525] rounded-2xl text-xl text-start">
{task.task1}
</p>
<p className="h-fit px-10 my-2 py-2 bg-slate-200 dark:bg-[#252525] rounded-2xl text-xl text-start">
{task.task2}
</p>
<p className="h-fit px-10 my-2 py-2 bg-slate-200 dark:bg-[#252525] rounded-2xl text-xl text-start">
{task.task3}
</p>
</div>
))
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.75, ease: "easeOut" }}
className="w-full min-h-full items-center font-Main text-[#121212] dark:text-[#ffffff] flex justify-between"
>
<div className="flex flex-col gap-20 h-full justify-around">
{/* <Progress value={50} label="Completed" className="bg-cyan-500" /> */}
{/* {isNaN(progressPercentage)?"Loading ...":<div className='h-5 w-full bg-gray-300'>
<div
style={{ width: `${progressPercentage}%`}}
className={`h-full ${
progressPercentage < 70 ? 'bg-red-600' : 'bg-green-600'}`}>
{progressPercentage}%
</div>
</div>} */}
<h1 className="text-6xl">Projects in progress</h1>
{projects.map((project) => (
<div ref={dragRef} key={project.id} className=" text-[#121212] dark:text-[#ffffff] flex flex-col gap-2" draggable={true} >
<div className="bg-slate-200 dark:bg-[#252525] w-[35rem] items-center h-32 rounded-2xl py-2 flex justify-between px-10" >
<div className="flex flex-col items-start justify-start gap-2" >
<h1 className="text-4xl text-[#018786] dark:text-[#03dac6] ">
{project.projects.title}
</h1>
<p className="text-lg text-[#12121247] dark:text-[#ffffff70]">
Due date :{" "}
<span className="text-[#121212c5] dark:text-[#ffffffb5] ">
{project.projects.dueDate}
</span>
</p>
</div>
<div className="text-5xl">
<button
onClick={() => {
setKey(project.title);
setDropDown(dropDown === false ? true : false);
}}
className="mx-5 dark:text-[#bb86fc] text-[#6200ee]"
>
{dropDown && key === project.title ? (
<ion-icon name="chevron-up-circle-outline"></ion-icon>
) : (
<ion-icon name="chevron-down-circle-outline"></ion-icon>
)}
</button>
<button className="text-[#018786] dark:text-[#03dac6]" onClick={()=>{
markProjectDone(project)
}}>
<ion-icon name="checkmark-circle-outline"></ion-icon>
</button>
</div>
</div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.75, ease: "easeOut" }}
className={`h-fit transition-all ${
dropDown && key === project.title ? "" : "hidden"
} w-full py-2`}
>
<div
className={`h-fit px-10 py-2 dark:bg-[#3700b3] bg-[#6200ee] rounded-2xl w-full flex flex-col justify-start`}
>
<div className="h-fit w-full ">
<p className="text-xl flex text-[#ffffffc1]">
Description :{" "}
<span className=" text-[#ffffff] ">
<p>{project.projects.description}</p>
</span>
</p>
</div>
<p>{project.projects.status}</p>
</div>
{tasksToShow}
</motion.div>
</div>
))}
</div>
<div className="flex flex-col gap-20 h-full justify-around">
<h1 className="text-6xl">Done Projects</h1>
{doneProjects.map((project) => (
<div className="">
<div className=" bg-[#0187878e] dark:bg-[#03dac579] w-[35rem] items-center h-32 rounded-2xl py-2 flex justify-between px-10">
<div className="flex flex-col items-start justify-start gap-2">
<h1 className="text-4xl text-[#121212]">{project.title}</h1>
<p className="text-lg text-[#12121247] ">
Due date :{" "}
<span className="text-[#121212c5] ">{project.dueDate}</span>
</p>
</div>
</div>
</div>
))}
</div>
</motion.div>
);
}
and this is the index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
import {DndProvider} from "react-dnd";
import {HTML5Backend} from "react-dnd-html5-backend";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<BrowserRouter>
<DndProvider backend={HTML5Backend}>
<App />
</DndProvider>
</BrowserRouter>
</React.StrictMode>
);
I tried putting the reference in different divs but still the same.

Lazy loading element in typescript hook not working

I've implemented a lazy loading hook, but for some reason the carousel component is not loading at all. I've got it from here: https://betterprogramming.pub/lazy-loading-in-next-js-simplified-435681afb18a
This is my child that I want to render when it's in view:
import { useRef } from "react";
import Carousel from "../components/carousel";
import useOnScreen from "../hooks/useOnScreen";
// home page
function HomePage() {
const carouselRef = useRef();
const carouselRefValue = useOnScreen(carouselRef);
return (
<div className="snap-y">
{/* 1 */}
<div className="flex justify-center items-start relative min-h-[calc(100vh_-_5rem)] bg-black snap-start ">
{/* Cone */}
<div className="absolute w-full max-w-full overflow-hidden min-w-fit cone animate-wiggle"></div>
<div className="grid justify-center grid-cols-4 max-w-7xl">
//Content
</div>
{/* 2 */}
<div className="z-20 pb-4 bg-black snap-start" ref={carouselRef.current}>
{carouselRefValue && <Carousel />}
</div>
{/* 3 */}
<div className="flex items-start justify-center py-32 min-h-fit bg-slate-50 snap-start">
//More content
</div>
);
}
export default HomePage;
useOnScreen hook:
import { useState, useEffect } from "react";
const useOnScreen = (ref: any) => {
const [isIntersecting, setIntersecting] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(([entry]) => setIntersecting(entry.isIntersecting));
if (ref.current) {
observer.observe(ref.current);
}
}, []);
return isIntersecting;
};
export default useOnScreen;
Edit: Needed to add const carouselRef = useRef() as React.MutableRefObject<HTMLInputElement>; paired with #asynts's answer.
This is incorrect:
<div className="z-20 pb-4 bg-black snap-start" ref={carouselRef.current}></div>
it should be:
<div className="z-20 pb-4 bg-black snap-start" ref={carouselRef}></div>

react js: infinite page rendering issue

So I have a page in react that is constantly rendering , say for eg. when I console log I can see that getting logged infinitely in a loop. is it some hook on some inner components that is getting rendered constantly I can't figure out
When i comment out
<CartContainer line_items = {line_items} />
it still doesnt solve the issue.
could useCartState() context be the issue?
Below is the page
import React, {useEffect} from "react"
import { CartContainer, NavigationBar } from "../components"
import {useRouter} from 'next/router'
import { useCartState } from "../context/Cart"
import { QuestionMarkCircleIcon } from '#heroicons/react/solid'
const getPaymentAPIStatus = async () =>
{
const response = await fetch("https://api.nowpayments.io/v1/status")
const data = await response.json()
}
const CartPage = () => {
const router = useRouter();
const {line_items, subtotal, total_items} = useCartState() // to get cart details --anaya
const isEmpty = line_items.length === 0
if(isEmpty)
return(
<div>
<NavigationBar/>
<p>Your cart is empty. Please add some items</p>
</div>
)
else {
return(
<div>
<NavigationBar/>
<div className="bg-white">
<div className="max-w-2xl mx-auto pt-16 pb-24 px-4 sm:px-6 lg:max-w-7xl lg:px-8">
<h1 className="text-3xl font-extrabold tracking-tight text-gray-900 sm:text-4xl">Shopping Cart</h1>
<form className="mt-12 lg:grid lg:grid-cols-12 lg:gap-x-12 lg:items-start xl:gap-x-16">
<section aria-labelledby="cart-heading" className="lg:col-span-7">
<h2 id="cart-heading" className="sr-only">
Items in your shopping cart
</h2>
<CartContainer line_items = {line_items} />
</section>
{/* Order summary */}
<section
aria-labelledby="summary-heading"
className="mt-16 bg-gray-50 rounded-lg px-4 py-6 sm:p-6 lg:p-8 lg:mt-0 lg:col-span-5"
>
<h2 id="summary-heading" className="text-lg font-medium text-gray-900">
Order summary
</h2>
<dl className="mt-6 space-y-4">
<div className="flex items-center justify-between">
<dt className="text-sm text-gray-600">Subtotal</dt>
<dd className="text-sm font-medium text-gray-900">{subtotal.formatted_with_symbol}</dd>
</div>
<div className="border-t border-gray-200 pt-4 flex items-center justify-between">
<dt className="flex items-center text-sm text-gray-600">
<span>Shipping estimate</span>
<a href="#" className="ml-2 flex-shrink-0 text-gray-400 hover:text-gray-500">
<span className="sr-only">Learn more about how shipping is calculated</span>
<QuestionMarkCircleIcon className="h-5 w-5" aria-hidden="true" />
</a>
</dt>
<dd className="text-sm font-medium text-gray-900">Free</dd>
</div>
<div className="border-t border-gray-200 pt-4 flex items-center justify-between">
<dt className="text-base font-medium text-gray-900">Order total</dt>
<dd className="text-base font-medium text-gray-900">{subtotal.formatted_with_symbol}</dd>
</div>
</dl>
<div className="mt-6">
<button
type="submit"
className="w-full bg-indigo-600 border border-transparent rounded-md shadow-sm py-3 px-4 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-50 focus:ring-indigo-500"
>
Checkout
</button>
</div>
<div className="mt-6 text-sm text-center">
<p>
or{' '}
<a href ={`${router.basePath}/ChooseProduct`} className="text-indigo-600 font-medium hover:text-indigo-500">
Continue Designing<span aria-hidden="true"> →</span>
</a>
</p>
</div>
</section>
</form>
</div>
</div>
</div>
)
}
}
export default CartPage
How do i solve this?
Thanks in advance!
Adding useCartState as requested
import {createContext, useEffect, useContext, useReducer} from 'react'
import {commerce} from '../../lib/commerce'
//need to correct this file to be a tsx file in the future
//Provides a context for Cart to be used in every page
const CartStateContext = createContext()
const CartDispatchContext = createContext()
const SET_CART = "SET_CART"
const initialState = {
total_items: 0,
total_unique_items: 0,
subtotal:[],
line_items: [{}]
}
const reducer = (state,action) => {
switch(action.type){
case SET_CART:
return { ...state, ...action.payload }
default:
throw new Error(`Unknown action: ${action.type}` )
}
}
export const CartProvider = ({children}) => {
const [state, dispatch] = useReducer(reducer, initialState)
const setCart = (payload) => dispatch({type: SET_CART, payload})
useEffect(() => {
getCart()
},[state]) // Getting the page to be rendered whenever the cart objects are changed
const getCart = async() => {
try {
const cart = await commerce.cart.retrieve()
setCart(cart)
} catch (error){
console.log("error")
}
}
return (
<CartDispatchContext.Provider value = {{setCart}}>
<CartStateContext.Provider value = {state}>
{children}
</CartStateContext.Provider>
</CartDispatchContext.Provider>
)
}
export const useCartState = () => useContext (CartStateContext)
export const useCartDispatch = () => useContext (CartDispatchContext)
The problem here is that you have a useEffect with state as a dependency, meaning that every time the state value is altered, the getCart function gets called.
Meanwhile, getCart sets the state within it.
Therefor, it creates a cycle where getCart sets the state, the useEffect callback runs as a result, getCart gets called again, sets the state again, and so on.
The bottom line is that you can not set the state inside of a function and call that function every time the state value is altered.

Resources