I apologize if this has been asked before, but I haven't found anything to help me quite yet, and I am new to React!
I want a value to increase every n
number of seconds a user holds down a button. Right now it is just increasing linearly ever 35 milliseconds!
So far I have something like this:
function Increaser() {
const [value, setValue] = useState(0);
const changeTimer: React.MutableRefObject<any> = useRef(null);
function timeoutClearUp() {
clearInterval(changeTimer.current);
}
function increment() {
changeTimer.current = setInterval(() => setValue(prev => prev + 1), 35);
};
function onChange(e: any) {
setValue(parseInt(e.target.value));
}
return (
<div>
<button onMouseUp={timeoutClearUp} onMouseDown={increment}>INCREASE</button>
<input
type="number"
value={value}
onChange={(e) => {onChange(e)}}
/>
</div>
);
}
I have tried adding another ref
but it didn't seem to work! What is a small thing I can add to this code to ensure that every second value
is incremented by a larger and larger value (the longer a user holds the button down for).
Thanks so much!
- React: ^17.0.2
- Typecript: ^4.5.5
I apologize if this has been asked before, but I haven't found anything to help me quite yet, and I am new to React!
I want a value to increase every n
number of seconds a user holds down a button. Right now it is just increasing linearly ever 35 milliseconds!
So far I have something like this:
function Increaser() {
const [value, setValue] = useState(0);
const changeTimer: React.MutableRefObject<any> = useRef(null);
function timeoutClearUp() {
clearInterval(changeTimer.current);
}
function increment() {
changeTimer.current = setInterval(() => setValue(prev => prev + 1), 35);
};
function onChange(e: any) {
setValue(parseInt(e.target.value));
}
return (
<div>
<button onMouseUp={timeoutClearUp} onMouseDown={increment}>INCREASE</button>
<input
type="number"
value={value}
onChange={(e) => {onChange(e)}}
/>
</div>
);
}
I have tried adding another ref
but it didn't seem to work! What is a small thing I can add to this code to ensure that every second value
is incremented by a larger and larger value (the longer a user holds the button down for).
Thanks so much!
- React: ^17.0.2
- Typecript: ^4.5.5
- 2 Why not have another hook that is incremented at the same time? i.e. in the same setInterval... Another way (which is probably better) would be to have a recursive function with a custom promise function (acting like a sleep) then whenever the mouse es up a state update would be triggered and the recursive function would break/end. – Sharp Dev Commented Mar 7, 2022 at 0:27
3 Answers
Reset to default 5Because nobody gave you a real solution, here is one such way of doing what you want.
What I have done here is to use a refrence to the input, and have the value in your state updated once they stop clicking the increment button, or when they change the input manually.
In the increment function I use a setTimeout to create a custom interval, and this interval uses a math equation to determine what the next timeout/interval will be. The equation I used will go from 35ms (start) to 10ms (end) in 41 seconds and will not go below 10ms.
Here is a time chart of what I use in this example for speeding up the interval. (the x axis is time in seconds, and y axis is the delay in milliseconds)
import { useState, useRef } from 'react';
function Increaser() {
const [value, setValue] = useState(0);
const myInputRef = useRef<HTMLInputElement>(null);
let myTimeout: ReturnType<typeof setTimeout>;
let startTime: Date;
function stopInterval() {
clearTimeout(myTimeout!);//Stop the interval in essence
setValue(parseInt(myInputRef.current!.value!)||0) //Set the value of the input (in the mean time value is "floating")
}
function increment() {
startTime = new Date();//Initialize when we started increasing the button
myTimeout = setTimeout(function runIncrement() {//Start the timeout which is really a controlled interval
const val = (parseInt(myInputRef!.current!.value)||0)+1;//Increment the value of the ref's target
myInputRef!.current!.value = (val).toString()//Assign that incremented value to the ref
const now = new Date();
const delta = now.getTime() - startTime.getTime();
const n = 10 + (1/(Math.pow(1.1,delta/1000-(2*Math.log(5))/Math.log(1.1))));//Whatever
//The math function I have chosen above will go from 35 ms to 10 ms in 41 seconds.
//The function will not go lower than 10 ms and starts at 35 ms
//However you can use whatever function or math you want to get it to do as you wish.
//...
//whatever logic here you want to calculate when we will trigger the increment again.
//...
myTimeout = setTimeout(runIncrement, parseInt(n.toString()));//Increment again after the variable n milliseconds which we calculate above
},35);//Start the interval with an initial time of 35 milliseconds
//Now we know when the interval started, and we can
};
function onChange(e: any) {
setValue(parseInt(e.target.value));
}
return (
<div>
<button onMouseUp={stopInterval} onMouseDown={increment}>INCREASE</button>
<input
type="number"
onChange={(e) => {onChange(e)}}
ref={myInputRef}
/>
</div>
);
}
export default Increaser;
`
I appologize for the slight formatting that S.O. is doing here (in bination with my VS code editor).
Thanks to SharpInnovativeTechnologies' answer, I realized how dumb I was and found an easy solution by going:
function increment() {
const past = Date.now()
const rate = 500;
changeTimer.current = setInterval(() => {
const diff = Math.floor((Date.now() - past) / rate);
setValue(prev => prev + (1 + diff))
}, 50);
}
Where rate
is a sort of sensitivity adjustment.
So every 50ms The Date.now() - past
increases and boosts the value
with the setter.
If you want to grow every second, you should change
...
changeTimer.current = setInterval(() => setValue(prev => prev + 1), 35);
...
<input
type="number"
value={value}
/>
to
...
changeTimer.current = setInterval(() => setValue(prev => prev + 35), 35);
...
<input
type="number"
value={value/1000}
/>
because the second parameter of setInterval
is milliseconds.
Full code and demo:
import React, { useState, useRef } from "react";
export default function Increaser() {
const [value, setValue] = useState(0);
const changeTimer = useRef(null);
function timeoutClearUp() {
if (changeTimer.current) {
clearInterval(changeTimer.current);
changeTimer.current = null;
}
}
function increment() {
changeTimer.current = setInterval(() => {
setValue((prev) => prev + 35);
}, 35);
}
return (
<div>
<button onMouseUp={timeoutClearUp} onMouseDown={increment}>
INCREASE
</button>
<button
onClick={() => {
setValue(0);
}}
>
Reset
</button>
<div>{value / 1000}</div>
</div>
);
}