反应测试挂钩与回调

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)));

但有那么糟糕吗?

钩子的问题是清理会失败,因为addEventListenerremoveEventListener回调不同。它们应该具有相同的功能:

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);