I am doing a small project with react lifecycles and I am attempting to change all box colors randomly every time the counter reaches a number divisible by 5 however, the boxes only change when the number is divisible by 10.
App.js:
import React from 'react';
import './App.css';
import Counter from "./components/Counter";
import Box from "./components/Box";
function App() {
const randomColor = [
"#0d0d8c",
"#a41313",
"#064826",
"#996e00"
];
const [number, setNumber] = React.useState(0);
const [boxes, setBoxes] = React.useState([]);
const [color, setColor] = React.useState('red');
const [change, setChange] = React.useState(false);
let box = boxes.map((obj, idx) =>
<Box key={idx} color={color} />
);
let getColor = () => {
if (boxes.length % 5 === 0) {
let rand = Math.floor(Math.random() * randomColor.length);
setColor(randomColor[rand]);
return randomColor[rand];
}
return color;
};
React.useEffect(() => {
if (boxes.length % 5 === 0) {
let rand = Math.floor(Math.random() * randomColor.length);
setColor(randomColor[rand]);
return randomColor[rand];
}
return color;
}, [color])
React.useEffect(() => {
if (number % 2 === 0) {
let newBoxList = [...boxes];
newBoxList.push({color: getColor()});
setBoxes(newBoxList);
}
}, [number,change]);
let reset = () => {
setNumber(0);
setBoxes([]);
setChange(true);
};
return (
<div className="App">
<button onClick={() => setNumber(number + 1)}>Increase</button>
<button onClick={reset}>reset</button>
<Counter count={number}/>
<div className="boxes">{box}</div>
</div>
);
}
export default App;
deployed site: https://priceless-spence-9ae99a.netlify.app/
I think the culprit is your useEffect() hook listening to number. Whenever number is even you are copying the existing contents of boxes and then adding new boxes to the existing boxes; you are not actually updating all boxes in the array. It seems your desire is to have all of those boxes, existing and new, be updated to a new color so you'll need to iterate through the array and update each.
I think it comes down to the fact that you are assigning the return value of getColor to your new boxes rather than the value of local state color.
Related
document.getElementById("cards").onmousemove = e => {
for(const card of document.getElementsByClassName("card")) {
const rect = card.getBoundingClientRect(),
x = e.clientX - rect.left,
y = e.clientY - rect.top;
card.style.setProperty("--mouse-x", `${x}px`);
card.style.setProperty("--mouse-y", `${y}px`);
};
}
I actually don't know how to use the above code in react js. so, if anyone knows please respond!
full source code link:
https://codepen.io/Hyperplexed/pen/MWQeYLW
to use Hook you need to handle with reference of element like this
const CardRef = React.useRef(null);
useShadow(CardRef);
return <div ref={CardRef} className="card" ></div>
And the hook would be something like this
import { useEffect } from 'react';
const useShadow = (reference: React.MutableRefObject<any>) => {
useEffect(() => {
const eventReference = (e) => {
const rect = reference.current.getBoundingClientRect(),
x = e.clientX - rect.left,
y = e.clientY - rect.top;
reference.current.style.setProperty('--mouse-x', `${x}px`);
reference.current.style.setProperty('--mouse-y', `${y}px`);
};
if (reference.current) {
const { current } = reference;
current.addEventListener('mousemove', eventReference);
}
return () => {
reference.current &&
reference.current.removeEventListener('mousemove', eventReference);
};
}, [reference]);
};
export default useShadow;
First of all, React does provide SyntheticEvents, so your onmousemove would probably look like this in React:
<div onMouseMove={ yourEventHandler } />
I can see what you are trying to do is to set the children .card's properties when the mouse had moved. What you can do is to have useState() in the parent .cards container to store the latest mouse position, then pass that state as props into the children. Something like:
export function Cards() {
const [mouseX, setMouseX] = useState(0);
const [mouseY, setMouseY] = useState(0);
const myOnMouseMove = (e)=> {
// set your state using setMouseX(), setMouseY()
}
return (
<div className='cards' onMouseMove={myOnMouseMove}>
<Card className='card' mouseX={mouseX} mouseY={mouseY} />
<Card className='card' mouseX={mouseX} mouseY={mouseY} />
...
</div>
)
}
(Not real implementation, just the concept)
I am trying to implement react counter. every 10 seconds I need to update label and progress bar. But in label I could able to display 1 to 6 in 60 seconds successfully. but in timer due to some issue even though it reaches 60 seconds progress bar percange showing 80% only.
timer logic
const [number, setNumber] = useState(0);
const [progBarVal, setProgBarValr] = useState(0);
useEffect(() => {
if (number >= 6) {
return;
}
const intervalID = setTimeout(() => {
setNumber((t) => t + 1);
setProgBarValr((t) => t + 10);
}, 1000);
return () => clearInterval(intervalID);
}, [number, progBarVal]);
with in the return statement
return{
<div > {number}/6 </div>
<Progress done={progBarVal} />
}
progress bar logic
import React, { useState } from 'react';
import './progress.scss';
const Progress = ({ done }) => {
const [style, setStyle] = useState({});
setTimeout(() => {
const newStyle = {
opacity: 1,
width: `${done}%`,
};
setStyle(newStyle);
}, 200);
return (
<div className='met-prog__progress'>
<div className='met-prog__progress-done' style={style}>
{done}%
</div>
</div>
);
};
export default Progress;
I am trying to do if number is 1 the progBarVal 10 like that.
someone help me to understand where it went wrong.
I want to create an array of non-overlapping random numbers with a length of 20.
However, when executed, the array is not complete and sometimes an error message "Too many re-renders" appears.
import React, {useState, useEffect} from "react";
const Ques = () => {
const [randArr, setRandArr] = useState([]);
const [randNum, setRandNum] = useState(0);
let rand = Math.floor(Math.random() * (19-0));
if(randArr.length !== 20 && randArr.includes(randNum) === true){
rand = Math.floor(Math.random() * (19-0));
setRandNum(rand)
}
useEffect(() =>{
setRandNum(rand);
setRandArr([...randArr, randNum]);
console.log("randNum : ", randNum);
console.log("randArr : ", randArr);
},[rand]);
return (
<div>
<button>Start</button>
</div>
);
};
export default Ques;
As has been pointed out in comments, you are declaring a new rand value each render cycle and using it as a dependency for an useEffect hook which then also enqueues a state update and triggers a rerender... repeat ad nauseam.
Instead of trying to iterate and populate an array with "random" numbers by "guessing" if they've already been selected, it'd be better to start with an array of [1..20] and "shuffle" it.
Example:
const res = Array.from({ length: 20 }, (_, i) => i + 1).sort(() => Math.random() - 0.5);
console.log(res.join(","));
You can just initialize your state to this value. No need for any loops and useEffect hooks.
const [randArr, setRandArr] = useState(
Array.from({ length: 20 }, (_, i) => i + 1).sort(() => Math.random() - 0.5)
);
import React, { useState, useEffect } from "react";
const App = () => {
const [random, setRandom] = useState([]);
const getRandom = () => Math.floor(Math.random() * (19 - 0));
useEffect(() => {
const arr = [];
for (let i = 0; i < 20; i++) {
arr.push(getRandom());
}
setRandom(arr);
console.log(arr);
}, []);
return (
<div>
<div>{random.join(",")}</div>
<button>Start</button>
</div>
);
};
export default App;
I'm having troubles to set up react-infinite-scroller within my React component.
I do not want to fetch data via an API directly in my component with loadMore because I already got it from my IndexedDB.
So I want to use my Array dbScans (array with objects) and want to have infinite scroll with max. 3 items of the array.
I tried to create a loadProducts function to slice and concate my array that I want to render but I am getting overload errors, when I try to call it in the Infinite-Scroll component.
import React, { useEffect, useState } from 'react';
import InfiniteScroll from 'react-infinite-scroller';
export default function ProductHistory() {
const [dbScans, setDbScans] = useState<IProductClient[]>([]);
const [loadedProducts, setLoadedProducts] = useState([]);
useEffect(() => {
(async function getDataFromDB() {
setDbScans(await db.yourScans.toArray());
})();
}, []);
let productHistory = dbScans;
// This function is not working
const loadProducts = (page: number) => {
const perPage = 3;
const moreItems: any = dbScans.slice(
page * perPage - perPage,
page * perPage + 1
);
// Am I doing this right?
setLoadedProducts(loadedProducts.concat(moreItems));
}
return (
<>
<div className="product-history">
{ productHistory.length > 0 ?
<InfiniteScroll
pageStart={0}
loadMore={loadProducts(0)} // This is not working
hasMore={true}
loader={<div className="loader" key={0}>Loading ...</div>}
>
{productHistory.map((product: any) =>
<div className="product-history__item" key={product.id}>
<p>{product.name}
</div>
)}
</InfiniteScroll>
: ''
}
</div>
</>
)
}
You should introduce a state variable called as lastObjectPosition which will have the position of the last object that is being shown by the infinite scroll.
const perPage = 3;
const [lastObjectPosition , setLastObjectPosition ] = useState(0);
And then hasMore attribute should be set like this:
hasMore={lastObjectPosition < dbScans.length}
And finally you should modify loadProducts function like this,
const loadProducts = () => {
setLoadedProducts(currentProducts => {
return [
...currentProducts,
dbScans.slice(lastObjectPosition, lastObjectPosition+perPage)
]
});
setLastObjectPosition(currentValue => {
return currentValue + perPage
}
}
Here's a component that displays a number (initially 0) and a button that when clicked increments that number. If the number is 10, it also displays a celebratory message.
function App() {
const [count, setCount] = React.useState(0);
const [milestone, setMilestone] = React.useState(false);
const increment = () => {
setCount(count + 1);
if (count === 10) {
setMilestone(true);
} else {
setMilestone(false);
}
};
return (
<div className="App">
<h1>{count}</h1>
<button onClick={increment}>+1</button>
{milestone ? <h1>Happy aniversary!</h1> : null}
</div>
);
}
When I display this, very confusingly, the "Happy anniversary!" message displays when the number on display is eleven, and I can't figure out why.
Because count is ten only on the eleventh click.
you can change your increment function to:
const increment = () => {
const newCount = count + 1
setCount(newCount);
setMilestone(newCount === 10);
};
Or may be use setMilestone within a React.useEffect, like:
React.useEffect(() => {
setMilestone(count === 10);
}, [count])