【发布时间】:2021-08-27 17:11:20
【问题描述】:
我尝试为轮询创建一个自定义挂钩。
问题:
我的钩子在 useEffect 中使用了一个 while 循环,但似乎 while 循环永远不会中断,即使我将条件更改为 false。
代码沙箱重现问题:
https://codesandbox.io/s/clever-worker-jm1p5?file=/src/App.tsx
代码:
我有 2 个文件:
- usePolling.ts(这是我的钩子)
- App.tsx(我正在执行/调用钩子的地方)
usePolling.ts
/* eslint-disable no-await-in-loop */
import { useState, useCallback, useEffect } from "react";
export interface PollingOptions<T> {
fetchFunc: () => Promise<T> | undefined;
}
export const usePolling = <T>(
{ fetchFunc }: PollingOptions<T>,
interval: number
) => {
const [data, setData] = useState<T>();
const [error, setError] = useState<Error>();
const [condition, setCondition] = useState(true);
const stopPolling = useCallback(() => {
setCondition(false);
}, []);
const performPolling = useCallback(async () => {
try {
const res = await fetchFunc();
setData(res);
} catch (err) {
setCondition(false);
setError(err);
return;
} finally {
await new Promise((r) => setTimeout(r, interval));
}
}, [fetchFunc, interval]);
useEffect(() => {
(async () => {
while (condition) {
await performPolling();
}
})();
return () => stopPolling();
}, [condition, performPolling, stopPolling, data]);
return { data, error, stopPolling };
};
App.tsx
import { useCallback, useMemo } from "react";
import { usePolling } from "./usePolling";
import "./styles.css";
export default function App() {
const fetchFunc = useCallback(async () => {
// please consider this as an API call
await new Promise((resolve) => setTimeout(resolve, 500));
return { isSigned: Math.random() < 0.5 };
}, []);
const { data, error, stopPolling } = usePolling({ fetchFunc }, 3000);
console.log(data, error, ">>>>>>>");
const isSigned = data?.isSigned;
const isContractSigned = useMemo(() => {
if (isSigned) stopPolling();
return isSigned || false;
}, [isSigned, stopPolling]);
return (
<div className="App">
<h1>{`Is contract signed: ${isContractSigned}`}</h1>
</div>
);
}
为什么我不使用 setInterval 而不是 while 循环
例如,如果我使用 setInterval,并且选择将轮询间隔更改为 100 毫秒。有3个问题:
- 很有可能在之前的 API 调用响应之前调用 API。
- 假设出于任何原因,第二个 API 调用在第一个调用之前响应,那么我就有麻烦了
- 不必要的 API 调用。
如果我使用while循环而不是setInterval,那么上面提到的所有问题都解决了。请注意,我仍然在 while 循环中使用 setTimeout,所以它会在下一次迭代之前等待指定的时间间隔。
预期的解决方案:
任何不使用 setInterval 的解决方案都是预期的。如果您建议对现有代码进行任何改进(甚至与此特定问题无关),欢迎您:)
谢谢!
【问题讨论】:
-
useEffect中的代码将在每次依赖项更改时运行。所以当condition发生变化时,useEffect里面的代码会再次运行,如果false不会执行while循环,但是useEffect第一次运行的while循环仍然会无限执行
标签: reactjs typescript react-typescript