Reputation: 1
How to limit and control the number of click on button per day (or per hour) in ReactJs
any help please
Upvotes: 0
Views: 963
Reputation: 33796
How to limit and control the number of click on button per day (or per hour) in ReactJs
You can't restrict this in React. I explained this in a comment:
This is something that you must validate in your private code (back end / server / etc.). You must assume that the user of your client (React) app has complete control over the view, state (JavaScript), network requests, etc., so any attempts to restrict actions on the client can be subverted/bypassed by the user.
So, you'll need to validate this in other private code that's not running in your client app.
With that out of the way, and acknowledging that the user can manipulate the state of your client app, let's explore an implementation of the kind of quota you asked about on the client (just for fun) with a custom hook: useQuota
:
Note that the hook doesn't use timers directly because timers on the order of hours/days are imprecise/unreliable.
<div id="root"></div><script src="https://unpkg.com/[email protected]/umd/react.development.js"></script><script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script><script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script><script>Babel.registerPreset('tsx', {presets: [[Babel.availablePresets['typescript'], {allExtensions: true, isTSX: true}]]});</script>
<script type="text/babel" data-type="module" data-presets="tsx,react">
// import ReactDOM from 'react-dom/client';
// import {
// StrictMode,
// useCallback,
// useRef,
// useState,
// type ReactElement,
// } from 'react';
// This Stack Overflow snippet demo uses UMD modules
// instead of the commented import statments above
const {
StrictMode,
useCallback,
useRef,
useState,
} = React;
type QuotaUsageResult = {
/**
* When your remaining value will increase next.
* Will always be a Date unless your quota limit is `0`.
*/
nextIncrease: Date | null;
/** Non-negative integer */
remaining: number;
/** Whther or not the quota usage succeeded */
success: boolean;
};
type QuotaCallback = (result: QuotaUsageResult) => void;
type QuotaOptions = {
/** Milliseconds. e.g. 10 seconds = `10_000` */
expirationInterval: DOMHighResTimeStamp;
/** Non-negative integer */
limit: number;
};
function useQuota (
{expirationInterval, limit}: QuotaOptions,
callback: QuotaCallback,
): () => void {
const [timeStamps, setTimeStamps] = useState<EpochTimeStamp[]>([]);
return useCallback(() => {
const now = Date.now();
const timeStampLimit = now - expirationInterval;
const notExpired = [...timeStamps.filter(ts => ts > timeStampLimit), now];
const quotaExceeded = notExpired.length > limit;
if (quotaExceeded) while (notExpired.length > limit) notExpired.pop();
setTimeStamps(notExpired);
const [oldest] = notExpired;
const nextIncrease = oldest ? new Date(now + oldest - timeStampLimit) : null;
const result: QuotaUsageResult = {
nextIncrease,
remaining: limit - notExpired.length,
success: !quotaExceeded,
};
callback(result);
}, [callback, limit, expirationInterval, timeStamps]);
}
function App (): ReactElement {
const timerIdRef = useRef(0);
const [buttonText, setButtonText] = useState('Next');
const [info, setInfo] = useState('Click the button to begin');
// Set the limit to 4 actions, every 10s
const onClick = useQuota({expirationInterval: 10e3, limit: 4}, (result) => {
const {nextIncrease, remaining, success} = result;
// Cancel previous timer
clearTimeout(timerIdRef.current);
// Indicate whether actions remain
setButtonText(remaining === 0 ? 'Wait' : 'Next');
let updatedInfo = `Success: ${success ? 'true' : 'false'}`;
updatedInfo += `, Remaining: ${remaining}`;
if (nextIncrease) {
updatedInfo += `, Next increase at: ${nextIncrease.toLocaleTimeString()}`;
// Set a timer to update the button text the next time the quota increases
const msUntilNextIncrease = nextIncrease.getTime() - Date.now();
timerIdRef.current = setTimeout(() => setButtonText('Next'), msUntilNextIncrease);
}
setInfo(updatedInfo);
},
);
return (
<div>
<div>{info}</div>
<button {...{onClick}}>{buttonText}</button>
</div>
);
}
const reactRoot = ReactDOM.createRoot(document.getElementById('root')!);
reactRoot.render(
<StrictMode>
<App />
</StrictMode>
);
</script>
Upvotes: 1