我有一个组件
StatusIndicator
,它使用 Redux Toolkit 查询 q
来获取状态值并显示它。
我想编写一个自定义挂钩
usePolling
,可以使用参数进行配置以定期调用 q
,更新 StatusIndicator
。
我有两个问题:
isAuthenticated
的依赖,自定义挂钩的内容也会重复运行。timeoutId
。这是正确的吗?export const StatusIndicator = () => {
const isAuthenticated = useSelector((state) => state.auth.token);
const { data, q } = useGetStatusQuery();
usePolling({ callback: q }, [isAuthenticated]);
return (<div>{{data.status}}</div>);
};
export const usePolling = ({ callback }, dependencies = []) => {
const [instanceTimeoutId, setInstanceTimeoutId] = useState(null);
// I only want this run when `isAuthenticated` changes
useEffect(() => {
(async function continuation() {
clearTimeout(instanceTimeoutId);
await callback();
const { timeoutId, promise } = delay(interval);
setInstanceTimeoutId(timeoutId);
await promise;
continuation();
})();
return () => clearTimeout(instanceTimeoutId);
}, [...dependencies]);
};
正如我在评论中提到的,您使用
useState
作为超时句柄,因此每次调用 continuation
函数时,它都会调用 setInstanceTimeoutId
,这将触发 usePolling
的重新渲染
由于状态改变而挂钩。
事实上,这里只需要一个简单的变量就足够了。您可能可以使用
useRef
作为句柄,但我认为简单的 let timeoutHandle
应该没有问题,因为我们只在两个地方使用该值 - 在 continuation
内和钩子清理中。两者都只会访问当时的当前值。
export const usePolling = ({ callback }, dependencies = []) => {
useEffect(() => {
let timeoutHandle = null;
(async function continuation() {
clearTimeout(timeoutHandle);
await callback();
const { timeoutId, promise } = delay(interval);
timeoutHandle = timeoutId;
await promise;
continuation();
})();
return () => clearTimeout(timeoutHandle);
}, [...dependencies]);
};