Issue
I'm implementing an input number using React. This input number has a + and - button that increases or decreases the value inside an input.
The onClick
methods works fine, however, I wonder, what's the method to keep triggering the increase
or decrease
methods while the button keeps pressed.
Here are my methods to update the state:
const increaseValue = () => {
const val = isNaN(tempValue) || tempValue === '' ? 0 : Number(tempValue)
setTempValue(val + 1)
}
const decreaseValue = () => {
const val = isNaN(tempValue) || tempValue === '' ? 0 : Number(tempValue)
setTempValue(val - 1)
}
and here's the component
<div className="flex flex-row h-8 w-20 rounded-lg relative bg-transparent">
<button
className=" bg-action text-action-100 hover:text-gray-300 hover:bg-action-300 h-full w-20 rounded-l cursor-pointer outline-none active:bg-action-200"
onClick={decreaseValue}
>
<span className="m-auto text-md font-thin">−</span>
</button>
<input
className="outline-none focus:outline-none text-center w-full bg-action font-thin text-xs antialiased flex items-center text-white"
name="custom-input-number"
value={tempValue}
onChange={e => {
setTempValue(e.target.value)
type === 'number' && setIsEditing(true)
}}
></input>
<button
className="bg-action text-action-100 hover:text-gray-300 hover:bg-action-300 h-full w-20 rounded-r cursor-pointer active:bg-action-200"
onClick={increaseValue}
>
<span className="m-auto text-md font-thin">+</span>
</button>
</div>
Now, I want to add the feature to keep increasing/decreasing while the button is still pressed.
Solution
The simplest way to do this is probably to keep track of when a button is first pressed and then when it is released, and using a useRef
to keep track of a time interval for how often the button action should trigger. That will look like this:
const intervalRef = useRef(null);
Then we need a function to handle when we begin pressing the plus button:
const startCountUp = () => {
if (intervalRef.current) return;
intervalRef.current = setInterval(() => {
setTempValue((prevCounter) => prevCounter + 1);
}, 10);
};
We'll add a similar function for when the user presses the minus button, which counts down:
const startCountDown = () => {
if (intervalRef.current) return;
intervalRef.current = setInterval(() => {
setTempValue((prevCounter) => prevCounter - 1);
}, 10);
};
Next, we need a way to stop incrementing the counter when we release the button. We'll create a function to handle stopping the timer:
const stopCounter = () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
};
Then we need to trigger our functions properly when the user interacts with a button. Our button's onMouseDown
should call startCountUp
(for the + button), and when the user releases the mouse we want to use onMouseUp
to call stopCounter
so we stop incrementing the counter. Also, we want to be prepared for the case where the user moves their mouse outside of the button, so we use onMouseLeave
to also call stopCounter
.
All put together, here's what that looks like:
const [tempValue, setTempValue] = useState(0);
const intervalRef = useRef(null);
const startCountUp = () => {
if (intervalRef.current) return;
intervalRef.current = setInterval(() => {
setTempValue((prevCounter) => prevCounter + 1);
}, 10);
};
const startCountDown = () => {
if (intervalRef.current) return;
intervalRef.current = setInterval(() => {
setTempValue((prevCounter) => prevCounter - 1);
}, 10);
};
const stopCounter = () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
};
useEffect(() => {
return () => stopCounter(); // when App is unmounted we should stop counter
}, []);
return (
<div className="App">
<div>
<button
onMouseDown={startCountDown}
onMouseUp={stopCounter}
onMouseLeave={stopCounter}
>
<span>−</span>
</button>
<input
name="custom-input-number"
value={tempValue}
onChange={(e) => {
setTempValue(e.target.value);
}}
></input>
<button
onMouseDown={startCountUp}
onMouseUp={stopCounter}
onMouseLeave={stopCounter}
>
<span>+</span>
</button>
</div>
</div>
);
And here's a full example of that on CodeSandbox:
Answered By - Andrew Hulterstrom
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.