在React中将值从Timer传递给它的父节点



我有一个小问题,不能正常工作。我有一个从10分钟开始的计时器,之后我想把我的父值从true更新到false。我正在尝试一些解决方案,我已经找到了,但我没有得到这个工作。

我Timer.tsx:

import React, { useEffect, useState } from "react";
import "./Timer.scss";
interface ITimerProps {
isValid?: boolean;
}
export const Timer = ({ isValid }: ITimerProps) => {
const [minutes, setMinutes] = useState(0);
const [seconds, setSeconds] = useState(0);
const tokenTTL = 10;
let difference;
useEffect(() => {
const actualTime = new Date();
const target = new Date(
actualTime.setMinutes(actualTime.getMinutes() + tokenTTL)
);
const interval = setInterval(() => {
const now = new Date();
difference = target.getTime() - now.getTime();
let m: string | number = Math.floor(
(difference % (1000 * 60 * 60)) / (1000 * 60)
);
m = m < 10 ? "0" + m : m;
setMinutes(m as number);
let s: string | number = Math.floor((difference % (1000 * 60)) / 1000);
s = s < 10 ? "0" + s : s;
setSeconds(s as number);
if (difference === 0) {
isValid= true;
}
}, 1000);
return () => clearInterval(interval);
}, []);
return (
<div className="Timer">
<div className="Timer__Container">
<div className="Timer-content">
<span className="minutes">{minutes}</span>
</div>
<span className="divider">:</span>
<div className="Timer-content">
<span className="seconds">{seconds}</span>
</div>
</div>
</div>
);
};

我是通过isValid从我的父母。所以我的父组件有一个isCodeExpired,我从我的钩子。

export const Login = () => {
const { t } = useTranslation();
const dispatch = useDispatch();
const logo = ResourceHelper.getResource(ResourceName.APP_LOGO);
const { code, refreshCode, isCodeExpired, refresh, error, loading } =
useLoginByCode();
useEffect(() => {
refreshCode();
}, []);
const handleCancelClick = () => {
dispatch(goBack());
};
const cancelButton = (
<Button
forceFocus
buttonClassName="LoginScreen-button-cancel"
onClick={handleCancelClick}
>
{t("MY_ORDERS__CANCEL")}
</Button>
);
return (
<>
{error && !loading ? (
<ErrorInfo
className="LoginScreen__Error"
forceFocus
onClick={refresh}
errorMessage={error.Message}
error={error}
/>
) : (
<div className="LoginScreen">
<img className="LoginScreen-logo" src={logo} alt="logo" />
<p className="LoginScreen-info">
{t("LOGIN_INFO", { LINK: `$t(LOGIN_INFO_LINK)` })}
</p>
{loading && (
<div className="LoginScreen-code">
<LoaderSpinner />
</div>
)}
{!isCodeExpired && !loading ? (
<>
<div className="LoginScreen-code">
<span>{code}</span>
<p className="LoginScreen-tokenInfo">
{t("TOKEN_VALID_INFO")}
{"  "} <Timer isValid={isCodeExpired} />
</p>
</div>
{cancelButton}
</>
) : (
<>
<Button
onClick={refreshCode}
buttonClassName="LoginScreen-button-refresh"
disabled={!isCodeExpired}
>
{t("REFRESH_CODE")}
</Button>
{cancelButton}
</>
)}
</div>
)}
</>
);
};

现在在这段代码中,我的isValid没有被更改为false。这是一些基本的问题,但我有问题得到这个工作。

你可以将一个回调函数传递给子组件,并在父组件上使用从子组件获得的值。

import React, { useEffect, useState } from "react";
import "./Timer.scss";
interface ITimerProps {
isValid?: Function;
}
export const Timer = ({ isValid }: ITimerProps) => {
const [minutes, setMinutes] = useState(0);
const [seconds, setSeconds] = useState(0);
const tokenTTL = 10;
let difference;
useEffect(() => {
const actualTime = new Date();
const target = new Date(
actualTime.setMinutes(actualTime.getMinutes() + tokenTTL)
);
const interval = setInterval(() => {
const now = new Date();
difference = target.getTime() - now.getTime();
let m: string | number = Math.floor(
(difference % (1000 * 60 * 60)) / (1000 * 60)
);
m = m < 10 ? "0" + m : m;
setMinutes(m as number);
let s: string | number = Math.floor((difference % (1000 * 60)) / 1000);
s = s < 10 ? "0" + s : s;
setSeconds(s as number);
if (difference === 0) {
isValid(true);
}
}, 1000);
return () => clearInterval(interval);
}, []);
return (
<div className="Timer">
<div className="Timer__Container">
<div className="Timer-content">
<span className="minutes">{minutes}</span>
</div>
<span className="divider">:</span>
<div className="Timer-content">
<span className="seconds">{seconds}</span>
</div>
</div>
</div>
);
};

您可以像下面这样从子组件接收值。

export const Login = () => {
const { t } = useTranslation();
const dispatch = useDispatch();
const [isCodeExpired, setIsCodeExpired]= useState(false);
const logo = ResourceHelper.getResource(ResourceName.APP_LOGO);
const { code, refreshCode, refresh, error, loading } =
useLoginByCode();
useEffect(() => {
refreshCode();
}, []);
const handleCancelClick = () => {
dispatch(goBack());
};
const cancelButton = (
<Button
forceFocus
buttonClassName="LoginScreen-button-cancel"
onClick={handleCancelClick}
>
{t("MY_ORDERS__CANCEL")}
</Button>
);
return (
<>
{error && !loading ? (
<ErrorInfo
className="LoginScreen__Error"
forceFocus
onClick={refresh}
errorMessage={error.Message}
error={error}
/>
) : (
<div className="LoginScreen">
<img className="LoginScreen-logo" src={logo} alt="logo" />
<p className="LoginScreen-info">
{t("LOGIN_INFO", { LINK: `$t(LOGIN_INFO_LINK)` })}
</p>
{loading && (
<div className="LoginScreen-code">
<LoaderSpinner />
</div>
)}
{!isCodeExpired && !loading ? (
<>
<div className="LoginScreen-code">
<span>{code}</span>
<p className="LoginScreen-tokenInfo">
{t("TOKEN_VALID_INFO")}
{"  "} <Timer isValid={(value) => setIsCodeExpired(value)} /> // Do whatever you want with the value you got from your child component
</p>
</div>
{cancelButton}
</>
) : (
<>
<Button
onClick={refreshCode}
buttonClassName="LoginScreen-button-refresh"
disabled={!isCodeExpired}
>
{t("REFRESH_CODE")}
</Button>
{cancelButton}
</>
)}
</div>
)}
</>
);
};

你需要传入你的isValid prop作为回调,因为一个变量传递的值不会更新父元素的值。

所以按照下面的方式重写你的子组件:

interface ITimerProps {
isValidCb?: (valid: boolean) => void;
}
export const Timer = ({ isValidCb }: ITimerProps) => {
// Your application code...
// Call the isValidCb function to update the value in parent
// And check if it exists, since its optional
...
if (difference === 0 && isValidCb) {
isValidCb(true);
}
...
}

然后你需要解析一个从父级到子级的回调函数。

export const Login = () => {
...
<Timer isValid={(isValid: boolean) => isCodeExpired = isValid} />
...
}

回调函数可以直接访问你的父属性,并且可以更新它们。这是一种从子组件更新父组件的方法。

最新更新