我有一个组件,当它可见时,我想每10秒查询一次API。当组件关闭/不可见时,我希望该轮询停止。
我的问题是,即使隐藏了组件,轮询仍会继续。无论我做什么,我的代码都无法识别新近更新的状态变量。
const DisplayState = Object.freeze({
DisplayGroupNamesForSelection: 0,
DisplayLogStream: 1
});
export default function LogStream({ isOpen }) {
const [currentState, setCurrentState] = useState(DisplayState.DisplayGroupNamesForSelection);
const [logEvents, setlogEvents] = useState([]);
const onStartStreamingButtonClicked = async logGroupName => {
setCurrentState(DisplayState.DisplayLogStream);
pollForLogs(logGroupName);
};
const pollForLogs = async logGroupName => {
try {
const newLogList = await getLogs(logGroupName); // API call elsehwere
setlogEvents(newLogList);
} catch(e) {
//
} finally {
// If the component is still open, ping the API again in 10s
// However, this always evaluates to true
if(isOpen) {
setTimeout(() => {
pollForLogs(logGroupName);
}, 10000);
}
}
};
return (
<>
{!isOpen ? (
<></>
) : (
<div>
{(() => {
switch(currentState) {
case DisplayState.DisplayGroupNamesForSelection: {
return (
<LogStreamingGroupSelector
onStartStreamingButtonClicked={onStartStreamingButtonClicked}
/>
);
}
case DisplayState.DisplayLogStream: {
return (
<div>
{logEvents.map((log, i) => (
<sub key={log.timestamp}>{log.message}</sub>
))}
</div>
);
}
}
})()}
</div>
)}
</>
);
}
我所能做的是一种有点棘手的解决方案,其中我在组件外部声明了一个变量,然后继续轮询取决于该变量:
let continuePollingForLogs = false;
export default function LogStream({ isOpen }) {
const [currentState, setCurrentState] = useState(DisplayState.DisplayGroupNamesForSelection);
const [logEvents, setlogEvents] = useState([]);
useEffect(() => {
continuePollingForLogs = isOpen;
}, [isOpen]);
...然后,如果我有pollForLogs
函数依赖于非状态变量continuePollingForLogs
,则它的行为确实符合预期。
我知道React在处理状态方面出了点问题,但我到底不能弄清楚什么。即使我创建了独立的useState
变量,但无论何时useEffect
进行更改,我都在isOpen
内进行了设置,该变量仍然不起作用。 pollForLogs()
的{{1}}中的变量始终为finally
。
答案 0 :(得分:2)
您可以使用useRef
创建一个可变的ref(变量),其状态(值)将在组件的整个生命周期中保持不变。然后,在卸载组件时,使用useEffect
中的cleanup函数将其设置为false。
import React, { useState, useRef } from 'react';
export default function LogStream({ isOpen }) {
const [currentState, setCurrentState] = useState(
DisplayState.DisplayGroupNamesForSelection
);
const [logEvents, setlogEvents] = useState([]);
const continuePollingForLogs = useRef(isOpen);
useEffect(() => {
return () => {
// set ref to false when component unmounts.
continuePollingForLogs.current = false;
};
});
}
答案 1 :(得分:1)
这是使用钩子时的错误方法。我建议任何将来的读者阅读Dan Abramov的文章,并使用其useInterval
自定义钩子:
https://overreacted.io/making-setinterval-declarative-with-react-hooks/