Initial commit
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
# useCountdown Hook
|
||||
|
||||
Створює таймер зворотного відліку.
|
||||
Автоматично викликає `onTimeOut` після завершення зворотного відліку.
|
||||
По замовчуванню `delay` 10s
|
||||
|
||||
Output:
|
||||
|
||||
```ts
|
||||
export type CountdownController = {
|
||||
start: () => void;
|
||||
dismiss: () => void;
|
||||
remaining: number | null;
|
||||
};
|
||||
```
|
||||
|
||||
```ts
|
||||
|
||||
export const EmailVerification: React.FC<{
|
||||
initialValues: FormValues;
|
||||
onSubmit: (values: FormValues) => void;
|
||||
}> = ({ initialValues, onSubmit }) => {
|
||||
const form = useForm<FormValues>({
|
||||
defaultValues: initialValues,
|
||||
resolver: zodResolver(verifySchema),
|
||||
});
|
||||
|
||||
const sendCodeMutation = useSendVerificationCode();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [countdownFinished, setCountdownFinished] = React.useState(false);
|
||||
const { start, remaining } = useCountdown({
|
||||
onTimeOut: () => setCountdownFinished(true),
|
||||
delay: 59 * 1000,
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
start();
|
||||
}, []);
|
||||
|
||||
const handleResendCode = () => {
|
||||
const email = form.getValues().email;
|
||||
|
||||
if (!email) return;
|
||||
|
||||
sendCodeMutation.mutate(
|
||||
{ email },
|
||||
{
|
||||
onSuccess() {
|
||||
toast.info(t('Verification code sent to email'));
|
||||
start();
|
||||
setCountdownFinished(false);
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form></Form>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
@@ -0,0 +1 @@
|
||||
export * from './useCountdown';
|
||||
@@ -0,0 +1,67 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export type UseCountdownParams = {
|
||||
onTimeOut: () => void;
|
||||
delay?: number;
|
||||
};
|
||||
|
||||
export type CountdownController = {
|
||||
start: () => void;
|
||||
dismiss: () => void;
|
||||
remaining: number | null;
|
||||
};
|
||||
|
||||
export const useCountdown = ({ onTimeOut, delay = 10000 }: UseCountdownParams): CountdownController => {
|
||||
const timeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const intervalRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const [remaining, setRemaining] = React.useState<number | null>(null);
|
||||
|
||||
const start = React.useCallback(() => {
|
||||
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
||||
if (intervalRef.current) clearInterval(intervalRef.current);
|
||||
|
||||
const totalSeconds = Math.ceil(delay / 1000);
|
||||
setRemaining(totalSeconds);
|
||||
|
||||
intervalRef.current = setInterval(() => {
|
||||
setRemaining((prev) => {
|
||||
if (prev === null || prev <= 1) {
|
||||
clearInterval(intervalRef.current!);
|
||||
return null;
|
||||
}
|
||||
return prev - 1;
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
onTimeOut();
|
||||
timeoutRef.current = null;
|
||||
setRemaining(null);
|
||||
}, delay);
|
||||
}, [delay, onTimeOut]);
|
||||
|
||||
const dismiss = React.useCallback(() => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
timeoutRef.current = null;
|
||||
}
|
||||
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
|
||||
setRemaining(null);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
||||
if (intervalRef.current) clearInterval(intervalRef.current);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { start, dismiss, remaining };
|
||||
};
|
||||
Reference in New Issue
Block a user