使用复杂对象的 useState 在 ReactJS 中无法按预期工作
Using useState of complex object not working as expected in ReactJS
我有一个功能组件,我正在为这样的复杂对象声明一个 useState
:
const [order, setOrder] = useState<IMasterState>({
DataInterface: null,
ErrorMsg: "",
IsRetrieving: true,
RetrievingMsg: "Fetching your order status..."
});
我现在尝试通过在 useEffect
中调用 setOrder
来设置 order
的状态,如下所示:
useEffect(() => {
(async function() {
let dh = new DataInterface("some string");
let errMsg = "";
// Get the sales order.
try
{
await dh.FetchOrder();
}
catch(error: any)
{
errMsg = error;
};
setOrder(salesOrder => ({...salesOrder, IsRetrieving: false, ErrorMsg: errMsg, DataInterface: dh}));
})();
}, []);
照原样,这似乎工作正常。但是,我有一个 setInterval
对象可以更改屏幕消息,而 order.IsRetrieving
是 true
:
const [fetchCheckerCounter, setFetchCheckerCount] = useState<number>(0);
const statusFetcherWatcher = setInterval(() => {
if (order.IsRetrieving)
{
if (fetchCheckerCounter === 1)
{
setOrder(salesOrder => ({...salesOrder, RetrievingMsg: "This can take a few seconds..."}));
}
else if (fetchCheckerCounter === 2)
{
setOrder(salesOrder => ({...salesOrder, RetrievingMsg: "Almost there!.."}));
}
setFetchCheckerCount(fetchCheckerCounter + 1);
}
else
{
// Remove timer.
clearInterval(statusFetcherWatcher);
}
}, 7000);
问题是该代码块的 order.IsRetrieving
始终是 true
,即使它确实更改为 false
,我的网站也会更改以反映这一点,甚至显示数据来自 dh.FetchOrder()
。这意味着我的计时器在后台无限循环。
那么我是否正确设置了 order
的状态?在网上很难找到一个明确的答案,因为所有的答案都是关于向数组中添加一个新项。
问题
- 您将间隔设置为函数体中的意外副作用。
- 您已经关闭了间隔回调中的初始
order.isRetreiving
状态值。
解决方案
使用挂载 useEffect
启动间隔并使用 React ref 在更新时缓存状态值,以便可以在异步回调中访问当前值。
const [order, setOrder] = useState<IMasterState>({
DataInterface: null,
ErrorMsg: "",
IsRetrieving: true,
RetrievingMsg: "Fetching your order status..."
});
const orderRef = useRef(order);
useEffect(() => {
orderRef.current = order;
}, [order]);
useEffect(() => {
const statusFetcherWatcher = setInterval(() => {
if (orderRef.current.IsRetrieving) {
if (fetchCheckerCounter === 1) {
setOrder(salesOrder => ({
...salesOrder,
RetrievingMsg: "This can take a few seconds...",
}));
} else if (fetchCheckerCounter === 2) {
setOrder(salesOrder => ({
...salesOrder,
RetrievingMsg: "Almost there!..",
}));
}
setFetchCheckerCount(counter => counter + 1);
} else {
// Remove timer.
clearInterval(statusFetcherWatcher);
}
}, 7000);
return () => clearInterval(statusFetcherWatcher);
}, []);
我有一个功能组件,我正在为这样的复杂对象声明一个 useState
:
const [order, setOrder] = useState<IMasterState>({
DataInterface: null,
ErrorMsg: "",
IsRetrieving: true,
RetrievingMsg: "Fetching your order status..."
});
我现在尝试通过在 useEffect
中调用 setOrder
来设置 order
的状态,如下所示:
useEffect(() => {
(async function() {
let dh = new DataInterface("some string");
let errMsg = "";
// Get the sales order.
try
{
await dh.FetchOrder();
}
catch(error: any)
{
errMsg = error;
};
setOrder(salesOrder => ({...salesOrder, IsRetrieving: false, ErrorMsg: errMsg, DataInterface: dh}));
})();
}, []);
照原样,这似乎工作正常。但是,我有一个 setInterval
对象可以更改屏幕消息,而 order.IsRetrieving
是 true
:
const [fetchCheckerCounter, setFetchCheckerCount] = useState<number>(0);
const statusFetcherWatcher = setInterval(() => {
if (order.IsRetrieving)
{
if (fetchCheckerCounter === 1)
{
setOrder(salesOrder => ({...salesOrder, RetrievingMsg: "This can take a few seconds..."}));
}
else if (fetchCheckerCounter === 2)
{
setOrder(salesOrder => ({...salesOrder, RetrievingMsg: "Almost there!.."}));
}
setFetchCheckerCount(fetchCheckerCounter + 1);
}
else
{
// Remove timer.
clearInterval(statusFetcherWatcher);
}
}, 7000);
问题是该代码块的 order.IsRetrieving
始终是 true
,即使它确实更改为 false
,我的网站也会更改以反映这一点,甚至显示数据来自 dh.FetchOrder()
。这意味着我的计时器在后台无限循环。
那么我是否正确设置了 order
的状态?在网上很难找到一个明确的答案,因为所有的答案都是关于向数组中添加一个新项。
问题
- 您将间隔设置为函数体中的意外副作用。
- 您已经关闭了间隔回调中的初始
order.isRetreiving
状态值。
解决方案
使用挂载 useEffect
启动间隔并使用 React ref 在更新时缓存状态值,以便可以在异步回调中访问当前值。
const [order, setOrder] = useState<IMasterState>({
DataInterface: null,
ErrorMsg: "",
IsRetrieving: true,
RetrievingMsg: "Fetching your order status..."
});
const orderRef = useRef(order);
useEffect(() => {
orderRef.current = order;
}, [order]);
useEffect(() => {
const statusFetcherWatcher = setInterval(() => {
if (orderRef.current.IsRetrieving) {
if (fetchCheckerCounter === 1) {
setOrder(salesOrder => ({
...salesOrder,
RetrievingMsg: "This can take a few seconds...",
}));
} else if (fetchCheckerCounter === 2) {
setOrder(salesOrder => ({
...salesOrder,
RetrievingMsg: "Almost there!..",
}));
}
setFetchCheckerCount(counter => counter + 1);
} else {
// Remove timer.
clearInterval(statusFetcherWatcher);
}
}, 7000);
return () => clearInterval(statusFetcherWatcher);
}, []);