我已经创建了一个自定义的倒计时组件使用react-countdown包。一般来说,它工作得很好。但是当我在页面中输入文本时,它会以某种方式再次呈现并重置为初始时间。我已经检查了输入的onChange事件,但它与倒计时无关。我真的很困惑为什么会这样。
我的想法是在创造倒计时是,如果我改变了关键 prop 的倒计时组件,我将有一个新的倒计时。因为我知道如果我们改变了react组件中的关键 prop ,它们会重新渲染。
倒计时组件:
const AgapeCountdown = ({ duration, children, restartKey, ...props }) => {
const classes = useStyles();
const defaultRenderer = ({ hours, minutes, seconds, completed }) => {
if (completed) {
return children;
}
return (
<span className={classes.root}>
{minutes}:{seconds}
</span>
);
};
return (
<Countdown
renderer={defaultRenderer}
date={Date.now() + duration}
key={restartKey}
{...props}
/>
);
};
使用方法:
<AgapeCountdown duration={10000} restartKey={countdownKey}>
<AgapeButton onClick={handleResendOtpClick} className={classes.textButton}>
ارسال مجدد کد
</AgapeButton>
</AgapeCountdown>;
在同一页中输入元素:
<AgapeTextField
placeholder="مثال: ۱۲۳۴۵"
variant="outlined"
fullWidth
onChange={handleOtpChange}
value={otp}
helperText={otpHelperText}
error={otpHelperText}
/>
输入更改处理程序:
const handleOtpChange = (event) => {
if (otpRegex.test(event.target.value)) {
setOtpHelperText(null);
setDisableOtpAction(false);
setOtp(event.target.value).then(() => {
nextButtonClicked();
});
} else {
setOtp(event.target.value);
setOtpHelperText(helperInvalidOtp);
setDisableOtpAction(true);
}
};
其中countdownKey得到更新:
const handleResendOtpClick = () => {
setCountdownKey(countdownKey + 1);
console.log('hello from resendotpclick');
registerApiService({
mobile: phoneNumberPure,
})
.then((response) => {
if (response.status === 200) {
// TODO show user that otp code resent.
}
})
.catch((error) => {
// TODO show user that otp code resend failed.
});
};
完整的代码进行更深入的检查:
const LoginStep2 = ({ dialogHandler, ...props }) => {
const classes = useStyles(props);
const setIsLoginOpen = dialogHandler;
const dispatch = useDispatch();
const phoneNumberPure = useSelector(selectPhone);
const ELogin = useSelector(selectELogin);
const [otp, setOtp] = useStateWithPromise(null);
const [otpHelperText, setOtpHelperText] = React.useState(null);
const [disableOtpAction, setDisableOtpAction] = React.useState(true);
const [phoneNumber, setPhoneNumber] = React.useState('');
const [countdownKey, setCountdownKey] = React.useState(1);
React.useEffect(() => {
if (phoneNumberPure) {
setPhoneNumber(phoneNumberPure.split('-')[1]);
}
}, [phoneNumberPure]);
const handlePrevIconClicked = () => {
if (ELogin) {
dispatch(next());
return;
}
dispatch(prev());
};
const nextButtonClicked = () => {
setDisableOtpAction(true);
const convertedOtp = convertPersianDigitsToEnglish(otp);
loginApiService({ mobile: phoneNumberPure, otp: convertedOtp })
.then((response) => {
if (response.status === 200) {
if (response.data.access_token) {
const jsonUser = {
phone: phoneNumberPure,
token: response.data.access_token,
social: null,
email: null,
};
localStorage.setItem('user', JSON.stringify(jsonUser));
if (ELogin) {
setIsLoginOpen(false);
return;
}
dispatch(next());
}
} else if (response.status === 404) {
setOtpHelperText(helperWrongOtp);
}
})
.catch((error) => {
setOtpHelperText(helperWrongOtp);
})
.finally(() => {
setTimeout(() => {
setDisableOtpAction(false);
}, 1000);
});
};
const handleResendOtpClick = () => {
setCountdownKey(countdownKey + 1);
console.log('hello from resendotpclick');
registerApiService({
mobile: phoneNumberPure,
})
.then((response) => {
if (response.status === 200) {
// TODO show user that otp code resent.
}
})
.catch((error) => {
// TODO show user that otp code resend failed.
});
};
const handleOtpChange = (event) => {
if (otpRegex.test(event.target.value)) {
setOtpHelperText(null);
setDisableOtpAction(false);
setOtp(event.target.value).then(() => {
nextButtonClicked();
});
} else {
setOtp(event.target.value);
setOtpHelperText(helperInvalidOtp);
setDisableOtpAction(true);
}
};
return (
<Grid container>
<Grid item xs={12}>
<IconButton onClick={handlePrevIconClicked}>
<BsArrowRight className={classes.arrowIcon} />
</IconButton>
</Grid>
<Grid
item
container
xs={12}
justify="center"
className={classes.logoContainer}
>
<img src={AgapeLogo} alt="لوگوی آگاپه" />
</Grid>
<Grid
item
container
xs={12}
justify="center"
className={classes.loginTitle}
>
<Typography variant="h4">کد تایید را وارد نمایید</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="body1" className={classes.noMargin}>
کد تایید به شماره
<span className={classes.phoneNumberContainer}>{phoneNumber}</span>
ارسال گردید
</Typography>
</Grid>
<Grid
item
container
xs={12}
justify="space-between"
className={classes.loginInputs}
>
<Grid item xs={12}>
<AgapeTextField
placeholder="مثال: ۱۲۳۴۵"
variant="outlined"
fullWidth
onChange={handleOtpChange}
value={otp}
helperText={otpHelperText}
error={otpHelperText}
/>
</Grid>
</Grid>
<Grid item xs={12}>
<AgapeButton
color="primary"
disabled={disableOtpAction}
onClick={nextButtonClicked}
fullWidth
>
تایید
</AgapeButton>
</Grid>
<Grid
item
container
xs={12}
justify="space-between"
className={classes.textButtonsContainer}
>
<Grid item xs={4}>
<AgapeCountdown duration={10000} restartKey={countdownKey}>
<AgapeButton
onClick={handleResendOtpClick}
className={classes.textButton}
>
ارسال مجدد کد
</AgapeButton>
</AgapeCountdown>
</Grid>
<Grid item xs={4} className={classes.callButton}>
<AgapeButton className={classes.textButton}>
دریافت از طریق تماس
</AgapeButton>
</Grid>
</Grid>
</Grid>
);
};
2条答案
按热度按时间r6hnlfcb1#
我发现问题了。这是真正产生问题的部分:
Date.now()
将更新。开始倒计时。为了解决这个问题,我使用了一个ref
,如果组件发生变化,它会停止重新渲染:yrdbyhpb2#
成功了!
基本上,在重新呈现时,Date.now()将捕获一个新的时间(重新呈现的时间),而不是“记住”倒计时开始的初始时间。
通过将其锁定在useRef中,即使在重新渲染之后,ref中的值也不会更改。