Hello everyone im trying to prevent double click but im not doing any API calls, and dont want to use timeout
I did few approachs
first one
import React, { useState } from "react";
const App = () => {
const [disabled, setDisabled] = useState(false);
const handleClick = () => {
return new Promise((resolve) => {
for (let i = 0; i < 99999999999; i++) {}
resolve(false);
});
};
return (
<div>
<button
disabled={disabled}
onClick={() => {
setDisabled(true);
handleClick().then((res) => {
setDisabled(res);
});
}}
>
{disabled ? "Disabled" : "Enable"}
</button>
</div>
);
};
export default App;
this approach didnt change my button to disabled and it blocked my main thread so its bad.
Second and third approachs are look like same but its not
I know click event order is (onMouseDown -> onMouseUp -> onClick) so i tried to put my setDisabled to both onMouseDown and onMouseUp
import React, { useState } from "react";
const App = () => {
const [disabled, setDisabled] = useState(false);
const handleClick = () => {
return new Promise((resolve) => {
for (let i = 0; i < 9999; i++) {}
resolve(false);
});
};
return (
<div>
<button
disabled={disabled}
onClick={() => {
console.log("onClick");
handleClick().then((res) => {
setDisabled(res);
});
}}
onMouseDown={() => {
console.log("mouseDown");
setDisabled(true);
}}
>
{disabled ? "Disabled" : "Enable"}
</button>
</div>
);
};
export default App;
But since my button is disabled onClick event not fired.
I can use debounce/setTimeout etc. but i dont want to use any hard-coded timeout values.
Is there any other way to make this?
To sum up;
I want my button disabled until my long for loop is finished after that again my button is enable
3
Answers
With the help of @SergeySosunov
The button was not changing to a disabled state because the UI thread was blocked by a CPU-intensive operation fired just after setDisabled call itself. This blocking prevents React from re-rendering with the new state. To resolve this, a timeout before the CPU operation was added, allowing React and the UI to have some time for re-rendering.
You could keep a state of
{ lastClick: number, disabled: boolean }
, only togglingdisabled
if the current click time exceeds last click by X seconds –Of course it’s a burden to tangle that logic with your component however. I would recommend a higher-order event handler like
usePreventDoubleClick
–Why do not You use backdropLoaders:-
You can create a React Mui Backdrop loader and when you once click on the button just set backdrop state to true and after your long press set it to the false state