【发布时间】:2022-01-06 12:49:25
【问题描述】:
我有一个投票钩子,它似乎在我的应用程序中工作。
import { useCallback, useEffect, useRef } from "react";
/**
* @param asyncFunction
* @param interval milliseconds between calls to the asyncFunction, defaults to a minute
* @param immediate if true it will run the asyncFunction immediately before looping
*/
export function usePolling(
asyncFunction: () => any | PromiseLike<any>,
interval = 60000,
immediate = true
) {
const timeoutRef = useRef<ReturnType<typeof setTimeout>>();
const mountedRef = useRef(false);
const activeRef = useRef(false);
const wrappedAsyncFunction = useCallback(async () => {
console.log("POLL", { mountedRef, activeRef });
if (!mountedRef.current || activeRef.current) {
// don't process if currently active or the component is unmounted
return;
}
activeRef.current = true;
try {
console.log("ABOUT TO ASYNC", { mountedRef, activeRef });
await asyncFunction();
console.log("ASYNC DONE", { mountedRef, activeRef });
} catch (e) {
console.error("Error while polling", e);
}
console.log("ABOUT TO SET TIMEOUT");
timeoutRef.current = setTimeout(wrappedAsyncFunction, interval);
console.log(" SET TIMEOUT", timeoutRef);
activeRef.current = false;
}, [mountedRef.current, activeRef.current, asyncFunction, interval]);
useEffect(() => {
mountedRef.current = true;
if (immediate) {
wrappedAsyncFunction();
} else {
timeoutRef.current = setTimeout(wrappedAsyncFunction, interval);
}
return () => {
clearTimeout(timeoutRef.current!);
mountedRef.current = false;
activeRef.current = false;
};
}, []);
return { activeRef };
}
我的测试看起来像这样
it("should work with just the callback", async () => {
jest.useFakeTimers();
const callback = jest.fn();
let renderCount = 0;
function MyComponent() {
usePolling(callback);
++renderCount;
return (<div data-testid="test">{renderCount}</div>);
}
const { getByTestId } = render(<MyComponent />)
expect(getByTestId("test").textContent).toEqual("1");
expect(renderCount).toEqual(1);
jest.runAllTicks();
expect(callback).toBeCalledTimes(1);
console.log("ABOUT TO RUN ALL TIMERS")
jest.runAllTimers();
console.log("RAN ALL TIMERS")
await waitFor(() => {
expect(callback).toBeCalledTimes(2);
expect(renderCount).toEqual(1);
});
jest.runAllTimers();
await waitFor(() => {
expect(callback).toBeCalledTimes(3);
expect(renderCount).toEqual(1);
});
})
我正在做一些断言
- 回调在任何超时之前被调用一次(因为它默认是
immediate = true) - 每次调用 runAllTimers() 时,都会调用回调。
- 没有重新渲染,因为我没有更新任何状态
如果没有模拟计时器,它似乎可以工作
it("should work with a real clock", async () => {
const callback = jest.fn();
let renderCount = 0;
function MyComponent() {
usePolling(callback, 500, true);
++renderCount;
return (<div data-testid="test">{renderCount}</div>);
}
const { getByTestId } = render(<MyComponent />)
expect(getByTestId("test").textContent).toEqual("1");
expect(renderCount).toEqual(1);
expect(callback).toBeCalledTimes(1);
await waitFor(() => {
expect(callback).toBeCalledTimes(2);
expect(renderCount).toEqual(1);
});
await waitFor(() => {
expect(callback).toBeCalledTimes(3);
expect(renderCount).toEqual(1);
});
})
我也尝试过以下操作,但没有运气......
...
expect(callback).toBeCalledTimes(1);
jest.runAllTimers();
jest.runAllTicks();
jest.runAllImmediates();
await waitFor(() => {
expect(callback).toBeCalledTimes(2); // fails here
expect(renderCount).toEqual(1);
});
...
【问题讨论】:
标签: javascript reactjs typescript jestjs