Duplicated setTimeout - reactjs

When I press the "Buy" button sometimes its grow up correctly and sometimes its just count by 2 clicks. Why this is happening? I am using UseState for change the values and setTimeout for passive increment, so when I press the Buy button its remove the older setTimeout and add a new with the new values.
import Head from 'next/head';
import styles from '../styles/Home.module.css';
import React, { useState, useEffect } from 'react';
var increment = 1000;
var preco = 10;
var total = 0;
function Master() {
const [points, setpoints] = useState(0);
const [Ppoints, setPpoints] = useState(0);
total = points;
function addpoints() {
setpoints((points) => points + 1);
window.clearTimeout(timer);
}
function addPpoints() {
setpoints((points) => points + Ppoints);
}
//passive addiction
let timer = setTimeout(addPpoints, increment);
function passive() {
if (total >= preco) {
setPpoints(Ppoints + 0.5);
setpoints(points - preco);
preco += 10;
window.clearTimeout(timer);
}
}
return (
<>
<h1>{total}</h1>
<button onClick={addpoints}>adicionar</button>
<h1>Price: {preco}</h1>
<button onClick={passive}>Buy</button>
</>
);
}
export default function Home() {
return (
<>
<Master />
</>
);
}

Related

React useState does not update after new array was assigned

I am trying to build a sorting visualizer in React.
Unfortunately, my useState doesn't update its value after a random array gets generated.
But in an earlier project it worked just fine.
I tried different ways to solve this issue but none of them worked.
import React, { useState } from "react";
// import "./SortingVisualizer.css";
export default function SortingVisualizer() {
const [numberArray, setNumberArray] = useState(null);
function resetArray() {
const array = [];
for (let i = 0; i < 100; i++) {
let num = randomIntFromInterval(5, 1000);
array.push(num);
console.log(array);
}
setNumberArray(array);
}
if (numberArray === null) resetArray();
return (
<div className="array-container">
{numberArray.map((value, idx) => {
return (
<div className="array-bar" key={idx}>
{value}
</div>
);
})}
</div>
);
}
function randomIntFromInterval(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}

changing Image for Every N seconds , using Hooks in React

import React, { useEffect, useState } from 'react'
import styles from './ImageChanging.module.css'
import ArrowLeftIcon from '#mui/icons-material/ArrowLeft';
import ArrowRightIcon from '#mui/icons-material/ArrowRight';
const img1 = require('../../assets/bg1.jpg')
const img2 = require('../../assets/bg2.jpg')
const img3 = require('../../assets/bg3.jpg')
export default function ImageChanging() {
let imageCollection = [img1, img2, img3]
// state
let [displayImageId,setDisplayImageId] = useState(0)
let [displayImage,setDisplayImage] = useState(imageCollection[displayImageId])
// try to reload
useEffect(() => {
setInterval(() => {
changeBg()
}, 8000)
}, [displayImage])
function changeBg() {
console.log(displayImage,"we are seeing the Starting Image")
if (displayImage < imageCollection.length - 1 ) {
setDisplayImageId((prevValue) => prevValue + 1)
setDisplayImage(imageCollection[displayImageId])
}
else {
console.log("we re inside ELse")
setDisplayImageId(0)
setDisplayImage(imageCollection[displayImageId])
}
}
function clickNextImage () {
console.log("U have Clicked Next Image")
if(displayImage == 3 ){
return
}
setDisplayImageId(displayImage + 1)
setDisplayImage(imageCollection[displayImageId])
}
function clickPrevImage() {
console.log("You have Clicked Previous Image")
if(displayImage == 0){
return
}
setDisplayImageId(displayImage - 1)
setDisplayImage(imageCollection[displayImageId])
}
return (
<div className={styles.ImageContainer}>
<ArrowLeftIcon className={styles.IconStyle} onClick={clickPrevImage}/>
<p>{displayImage}</p>
<img className={styles.Image} src={displayImage} />
<ArrowRightIcon className={[styles.IconStyle,styles.IconStyleRight]} onClick={clickNextImage}/>
</div>
)
}
i have imported all my images in my js File
I am running SetInterval inside UseEffect hook , to Change the image in my component
I am getting sometimes displayImageId as 3 and my images are flickring some Times
can Anyone Suggest why Id is gong to 3 what i am doing Wrong

Import function from another page

I need to call function resetToken() from another page when i click on button.
resetToken() should change useState to generate new code. I don't know how to import this function to another page and use it.
I have import
import Captcha from '../../../components/Captcha/Captcha'; and displayed with <Captcha/> in return( ... )
So when i click on button I need to call function resetToken() to generate new code or call again import because I have in <Captcha/>
React.useEffect(() => {
resetToken();
},[]);
This code is Captcha.jsx
import React from 'react';
import './Captcha.css';
function Captcha({statusOfCaptcha}){
const [status, setStatus] = React.useState(undefined);
const [code, setCode] = React.useState(undefined);
const [text, setText] = React.useState("");
const [seconds, setSeconds] = React.useState(120);
function resetToken(){
//generate code
var codeGenerated = "";
var possible = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz123456789";
for (var i = 0; i < 6; i++){
codeGenerated += possible.charAt(Math.floor(Math.random() * possible.length));
}
setCode(codeGenerated);
//reset every 120 second
setInterval(function(){
var codeGenerated = "";
var possible = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz123456789";
for (var i = 0; i < 6; i++){
codeGenerated += possible.charAt(Math.floor(Math.random() * possible.length));
}
setCode(codeGenerated);
setSeconds(120);
setStatus(undefined);
setText("");
}, 120000);
const interval = setInterval(() => {
setSeconds(seconds => seconds - 1);
}, 1000);
return () => clearInterval(interval);
}
React.useEffect(() => {
resetToken();
},[]);
function checkCaptcha(e){
if(e === code){
setStatus(true);
statusOfCaptcha(true);
} else{
setStatus(false);
statusOfCaptcha(false);
}
}
return (
<div className='captcha'>
<div className="background">
<p onCopy={(e) => e.preventDefault()} className="unselectable">{code}</p>
<a>{seconds}</a>
</div>
<div className='input-captcha'>
<input type="text" placeholder="Zadejte kód" value={text} onChange={(e) => {checkCaptcha(e.target.value); setText(e.target.value)}}/>
{status === false && (<i class='bx bx-x text-color-red'></i>)}
{status === true && (<i class='bx bx-check text-color-green'></i>)}
</div>
</div>
)
}
export default Captcha;
This code is index.jsx
import React from 'react'
import Captcha from '../../../components/Captcha/Captcha';
function Index() {
function change(){
//here i need to call function from Captcha.jsx - resetToken();
}
return (
<div>
<Captcha statusOfCaptcha={resCaptchaData}/>
<button onclick={change}>Reset captcha code</button>
</div>
)
}
export default Index
It would be better to use a custom hook, to store your state, and resetToken function, So you can use it in multiple places.
For more resources about custom hooks.
https://reactjs.org/docs/hooks-custom.html
You can do this in several ways
for example you can use state manager like context api or redux.
In order to have access to your states or functions everywhere and in all pages and components
Or you can put the resetToken function in the parent component and have access to it in the child components.
export const ParentComponent = (children) => {
function resetToken {
....
}
return (
<Recapcha resetToken={resetToken} />
)
}
const Recapcha = ({resetToken}) => {
return (...)
}

Calling react hook from a function on another page

I am making a chess game and I need to integrate a count down timer, e.g - so when it's the white players turn their clock is counting down until they move.
To avoid changing a lot more code that is not present in this question ,I would like to keep function "move" as is, though I could change CountDownTimer if needed,
For now I would like to know how to call the react hook setTimerOn in the CountDownTimer component from the function move,then I can work out the rest myself.
From what I have read you cannot do this with react hooks? so just looking for
anything that works, if that means changing CountDownTimer.
export function move(from, to, promotion) {
let tempMove = { from, to }
if (member.piece === chess.turn()) {
const legalMove = chess.move(tempMove)
if (legalMove) {
updateGame()
//i would like to access CountDownTimer and call setTimerOn(true)
// i can work out the code for white and black / start and stop myself afterwards
}
}
,
}
import React from 'react'
function CountDownTimer(){
const [time, setTime] = React.useState(0);
const [timerOn, setTimerOn] = React.useState(false);
React.useEffect(() => {
let interval = null;
if (timerOn) {
interval = setInterval(() => {
setTime((prevTime) => prevTime + 10);
}, 10);
} else if (!timerOn) {
clearInterval(interval);
}
return () => clearInterval(interval);
}, [timerOn]);
return (
<div className="Timers">
<h2>Stopwatch</h2>
<div id="display">
<span>{("0" + Math.floor((time / 60000) % 60)).slice(-2)}:</span>
<span>{("0" + Math.floor((time / 1000) % 60)).slice(-2)}:</span>
<span>{("0" + ((time / 10) % 100)).slice(-2)}</span>
</div>
<div id="buttons">
{!timerOn && time === 0 && (
<button onClick={() => setTimerOn(true)}>Start</button>
)}
{timerOn && <button onClick={() => setTimerOn(false)}>Stop</button>}
{!timerOn && time > 0 && (
<button onClick={() => setTime(0)}>Reset</button>
)}
{!timerOn && time > 0 && (
<button onClick={() => setTimerOn(true)}>Resume</button>
)}
</div>
</div>
);
});
export default CountDownTimer; ```
Based on React documentation, React Hook can be called from function components or other hooks.
In your situation, you should consider the utilization of React Context. You need to move up timerOn state and setTimerOn method as context values. So that, all components which are wrapped by the context provider can utilize the values.
First, create some helpers for managing context.
// TimerOn.js
import React, { createContext, useContext } from 'react';
// create a context
const TimerOnContext = createContext();
// create a hook to provide context value
export function useTimerOn() {
const contextValue = useContext(TimerOnContext);
return contextValue;
}
// custom provider that will wrap your main components
export function TimerOnProvider({ children }) {
const [timerOn, setTimerOn] = React.useState(false);
return (
<TimerOnContext.Provider value={{ timerOn, setTimerOn }}>
{children}
</TimerOnContext.Provider>
);
}
For instance, I create two simple components to demonstrate the timer component and caller component.
// CountDownTimer.js
import React from "react";
import { useTimerOn } from "./TimerOn";
export default function CountDownTimer() {
const { timerOn } = useTimerOn();
// detect changes
React.useEffect(() => {
if (timerOn) {
console.log('timer is on');
} else {
console.log('timer is off');
}
}, [timerOn]);
return (
<div>{timerOn ? 'timer on' : 'timer off'}</div>
);
}
// MoveCaller.js
import React from "react";
import { useTimerOn } from "./TimerOn";
export default function MoveCaller() {
const { timerOn, setTimerOn } = useTimerOn();
// move then set timer
const move = () => {
setTimerOn(!timerOn);
}
return (
<div>
<button type="button" onClick={move}>
Move
</button>
</div>
);
}
Then, you can wrap all main components with the provider component. So, the move function in a component can change the state of timerOn and be read by another component.
import React from 'react';
import CountDownTimer from './CountDownTimer';
import MoveCaller from './MoveCaller';
import { TimerOnProvider } from './TimerOn';
export default function ChessApp() {
return (
<TimerOnProvider>
<CountDownTimer />
<MoveCaller />
</TimerOnProvider>
);
}

React hook setState, setTimeout memory leak

import React, { useState, useEffect, useRef } from 'react';
import styles from './TextAnimation.module.scss';
const TextAnimation = () => {
const [typedText, setTypedText] = useState([
"Welcome to Byc",
"Change your Life"
]);
const [value, setValue] = useState();
const [inType, setInType] = useState(false);
let attachClasses = [styles.Blink];
if(inType) {
attachClasses.push(styles.Typing)
}
const typingDelay = 200;
const erasingDelay = 100;
const newTextDelay = 5000;
let textArrayIndex = 0;
let charIndex = 0;
const type = () => {
if(charIndex < typedText[textArrayIndex].length + 1) {
setValue(typedText[textArrayIndex].substring(0, charIndex));
charIndex ++;
setTime();
} else {
setInType(false);
setTimeout(erase, newTextDelay);
}
};
const setTime = () => {
setTimeout(type, typingDelay);
};
const erase = () => {
if(charIndex > 0) {
setValue(typedText[textArrayIndex].substring(0, charIndex - 1));
charIndex --;
setTimeout(erase, erasingDelay);
} else {
setInType(false);
textArrayIndex ++;
if(textArrayIndex >= typedText.length) {
textArrayIndex = 0;
}
setTimeout(type, newTextDelay - 3100);
}
};
useEffect(() => {
type();
}, [])
return (
<div className={styles.TextAnimation}>
<span className={styles.Text} >{value}</span><span className={attachClasses.join(' ')} > </span>
</div>
);
};
export default TextAnimation;
I'am trying to make text animation, but i got an message just like this...
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
How can i fix it?
You need to clear timeouts when your component unmounts, otherwise maybe a timeout will run after the component is unmounted.
To do that :
store the return value of each timeout in a list in some ref (with React.useRef for example)
return a callback in useEffect that clears the timeouts with clearTimeout(<return value of setTimeout>)

Resources