0

问题

我创建了一个自定义的 React 钩子,其中包含fetch一个AbortSignal和一个 loading state。(我完全了解我可以使用的大量库,但这是针对企业应用程序的,这是此用例的最佳方法。)

钩子按预期工作。它允许我在需要时调用包装的 fetch(甚至传递给React-Query),我可以按需调用中止调用,并根据加载状态更新 UI。

我可以很容易地测试钩子的返回值、被包装的 fetch 的调用、中止信号的功能;但是,我在loading仅使用 jest 测试状态时遇到了很大的麻烦,并且react-hooks-testing-library. Component如果可以避免,我不想为此测试创建一个。

我希望能够调用包装的 fetch,并将加载状态视为 true,然后在解析后,将加载状态视为 false。目前,加载状态始终返回为 false。

use-wrapped-fetch.ts

对于这个钩子的内部状态,我已经尝试过useRef, useState, useReducer,并且我在测试任何一种方式时都遇到了同样的问题。我也useCallback完全删除了相同的结果。

function useWrappedFetch<
    RequestBody = unknown,
    ResponseData = unknown,
>(): UseWrappedFetchState<RequestBody, ResponseData> {
    const abortController = React.useRef<AbortController | undefined>(undefined);
    const isFetching = React.useRef<boolean>(false); 

    const wrappedFetch = React.useCallback(async ({
        init,
        config,
    }: WrappedFetchInit<RequestBody>): Promise<ResponseData> => {
        abortController.current = new AbortController();
        isFetching.current = true;
        const method: Lowercase<keyof CoreFetch> = coreFetchMethod(config.method ?? 'GET');
        try {
            return fetch[method]<ResponseData>(init, {
                ...config,
                timeout: config.timeout ?? fetchTimeout,
                signal: abortController.current.signal,
            });
        } finally {
            isFetching.current = false;
        }
    }, []);

    return {
        abortController: abortController.current,
        wrappedFetch,
        isFetching: isFetching.current,
    };
}

use-wrapped-fetch.spec.ts

测试用于msw在服务工作者上启动模拟服务器。下面的测试在第一次期望时失败(返回false而不是期望true)。

it('sets isLoading to true on api call, then sets to false on resolve', async () => {
    // Given
    const config = deleteRequestConfig({});
    const init = 'https://internal.local/delete-short-delay';

    // When
    const { result, waitForNextUpdate } = renderHook(useWrappedFetch);

    act(() => {
        result.current.wrappedFetch({ init, config });
    });

    expect(result.current.isFetching).toBe(true);

    await waitForNextUpdate();

    expect(result.current.isFetching).toBe(false);
});
4

0 回答 0