反应测试挂钩与回调
React testing hook with callback
我正在尝试测试我为拦截 offline/online 事件而制作的简单挂钩:
import { useEffect } from 'react';
const useOfflineDetection = (
setOffline: (isOffline: boolean) => void
): void => {
useEffect(() => {
window.addEventListener('offline', () => setOffline(true));
window.addEventListener('online', () => setOffline(false));
return () => {
window.removeEventListener('offline', () => setOffline(true));
window.removeEventListener('online', () => setOffline(false));
};
}, []);
};
export default useOfflineDetection;
------------------------------------
//...somewhere else in the code
useOfflineDetection((isOffline: boolean) => Do something with 'isOffline');
但我不确定我是否使用了正确的方法来获得 return 值,而且我不确定如何开玩笑地测试它,@testing-library & @testing-library/react-hooks.
我误解了如何挂载我的钩子,然后捕获回调提供的 return。
有人可以帮助我吗?我受够了:'(
提前致谢!
编辑:
正如 Estus Flask 所说,我可以使用 useEffect 而不是回调,就像我首先设计的那样。
import { useEffect, useState } from 'react';
const useOfflineDetection = (): boolean => {
const [isOffline, setIsOffline] = useState<boolean>(false);
useEffect(() => {
window.addEventListener('offline', () => setIsOffline(true));
window.addEventListener('online', () => setIsOffline(false));
return () => {
window.removeEventListener('offline', () => setIsOffline(true));
window.removeEventListener('online', () => setIsOffline(false));
};
}, []);
return isOffline;
};
export default useOfflineDetection;
------------------------------------
//...somewhere else in the code
const isOffline = useOfflineDetection();
Do something with 'isOffline'
但是如果我想使用这个钩子来存储“isOffline”和 redux 或其他东西,我看到的唯一模式是使用 useEffect:
const isOffline = useOfflineDetection();
useEffect(() => {
dispatch(setIsOffline(isOffline));
}, [isOffline])
而不仅仅是:
useOfflineDetection(isOffline => dispatch(setIsOffline(isOffline)));
但有那么糟糕吗?
钩子的问题是清理会失败,因为addEventListener
和removeEventListener
回调不同。它们应该具有相同的功能:
const setOfflineTrue = useCallback(() => setOffline(true), []);
const setOfflineFalse = useCallback(() => setOffline(false), []);
useEffect(() => {
window.addEventListener('offline', setOfflineTrue);
...
然后可以使用 React Hooks 测试库来测试一个钩子。
由于 DOM 事件目标已经确定了 Jest DOM 在某种程度上支持的行为,因此可以分派相应的事件来测试回调:
const mockSetOffline = jest.fn();
const wrapper = renderHook(() => useOfflineDetection(mockSetOffline));
expect(mockSetOffline).not.toBeCalled();
// called only on events
window.dispatchEvent(new Event('offline'));
expect(mockSetOffline).toBeCalledTimes(1);
expect(mockSetOffline).lastCalledWith(false);
window.dispatchEvent(new Event('online'));
expect(mockSetOffline).toBeCalledTimes(2);
expect(mockSetOffline).lastCalledWith(true);
// listener is registered once
wrapper.rerender();
expect(mockSetOffline).toBeCalledTimes(2);
window.dispatchEvent(new Event('offline'));
expect(mockSetOffline).toBeCalledTimes(3);
expect(mockSetOffline).lastCalledWith(false);
window.dispatchEvent(new Event('online'));
expect(mockSetOffline).toBeCalledTimes(4);
expect(mockSetOffline).lastCalledWith(true);
// cleanup is done correctly
window.dispatchEvent(new Event('offline'));
window.dispatchEvent(new Event('online'));
expect(mockSetOffline).toBeCalledTimes(4);
我正在尝试测试我为拦截 offline/online 事件而制作的简单挂钩:
import { useEffect } from 'react';
const useOfflineDetection = (
setOffline: (isOffline: boolean) => void
): void => {
useEffect(() => {
window.addEventListener('offline', () => setOffline(true));
window.addEventListener('online', () => setOffline(false));
return () => {
window.removeEventListener('offline', () => setOffline(true));
window.removeEventListener('online', () => setOffline(false));
};
}, []);
};
export default useOfflineDetection;
------------------------------------
//...somewhere else in the code
useOfflineDetection((isOffline: boolean) => Do something with 'isOffline');
但我不确定我是否使用了正确的方法来获得 return 值,而且我不确定如何开玩笑地测试它,@testing-library & @testing-library/react-hooks.
我误解了如何挂载我的钩子,然后捕获回调提供的 return。
有人可以帮助我吗?我受够了:'(
提前致谢!
编辑:
正如 Estus Flask 所说,我可以使用 useEffect 而不是回调,就像我首先设计的那样。
import { useEffect, useState } from 'react';
const useOfflineDetection = (): boolean => {
const [isOffline, setIsOffline] = useState<boolean>(false);
useEffect(() => {
window.addEventListener('offline', () => setIsOffline(true));
window.addEventListener('online', () => setIsOffline(false));
return () => {
window.removeEventListener('offline', () => setIsOffline(true));
window.removeEventListener('online', () => setIsOffline(false));
};
}, []);
return isOffline;
};
export default useOfflineDetection;
------------------------------------
//...somewhere else in the code
const isOffline = useOfflineDetection();
Do something with 'isOffline'
但是如果我想使用这个钩子来存储“isOffline”和 redux 或其他东西,我看到的唯一模式是使用 useEffect:
const isOffline = useOfflineDetection();
useEffect(() => {
dispatch(setIsOffline(isOffline));
}, [isOffline])
而不仅仅是:
useOfflineDetection(isOffline => dispatch(setIsOffline(isOffline)));
但有那么糟糕吗?
钩子的问题是清理会失败,因为addEventListener
和removeEventListener
回调不同。它们应该具有相同的功能:
const setOfflineTrue = useCallback(() => setOffline(true), []);
const setOfflineFalse = useCallback(() => setOffline(false), []);
useEffect(() => {
window.addEventListener('offline', setOfflineTrue);
...
然后可以使用 React Hooks 测试库来测试一个钩子。
由于 DOM 事件目标已经确定了 Jest DOM 在某种程度上支持的行为,因此可以分派相应的事件来测试回调:
const mockSetOffline = jest.fn();
const wrapper = renderHook(() => useOfflineDetection(mockSetOffline));
expect(mockSetOffline).not.toBeCalled();
// called only on events
window.dispatchEvent(new Event('offline'));
expect(mockSetOffline).toBeCalledTimes(1);
expect(mockSetOffline).lastCalledWith(false);
window.dispatchEvent(new Event('online'));
expect(mockSetOffline).toBeCalledTimes(2);
expect(mockSetOffline).lastCalledWith(true);
// listener is registered once
wrapper.rerender();
expect(mockSetOffline).toBeCalledTimes(2);
window.dispatchEvent(new Event('offline'));
expect(mockSetOffline).toBeCalledTimes(3);
expect(mockSetOffline).lastCalledWith(false);
window.dispatchEvent(new Event('online'));
expect(mockSetOffline).toBeCalledTimes(4);
expect(mockSetOffline).lastCalledWith(true);
// cleanup is done correctly
window.dispatchEvent(new Event('offline'));
window.dispatchEvent(new Event('online'));
expect(mockSetOffline).toBeCalledTimes(4);