import { createSelectorCreator, lruMemoize } from '@reduxjs/toolkit' import { buildCreateApi, coreModule, reactHooksModule, } from '@reduxjs/toolkit/query/react' import { render, screen, waitFor } from '@testing-library/react' import { delay } from 'msw' import * as React from 'react' import type { ReactReduxContextValue } from 'react-redux' import { Provider, createDispatchHook, createSelectorHook, createStoreHook, } from 'react-redux' import { setupApiStore, useRenderCounter } from '../../tests/utils/helpers' const MyContext = React.createContext(null) describe('buildCreateApi', () => { test('Works with all hooks provided', async () => { const customCreateApi = buildCreateApi( coreModule(), reactHooksModule({ hooks: { useDispatch: createDispatchHook(MyContext), useSelector: createSelectorHook(MyContext), useStore: createStoreHook(MyContext), }, }), ) const api = customCreateApi({ baseQuery: async (arg: any) => { await delay(150) return { data: arg?.body ? { ...arg.body } : {}, } }, endpoints: (build) => ({ getUser: build.query<{ name: string }, number>({ query: () => ({ body: { name: 'Timmy' }, }), }), }), }) let getRenderCount: () => number = () => 0 const storeRef = setupApiStore(api, {}, { withoutTestLifecycles: true }) // Copy of 'useQuery hook basic render count assumptions' from `buildHooks.test.tsx` function User() { const { isFetching } = api.endpoints.getUser.useQuery(1) getRenderCount = useRenderCounter() return (
{String(isFetching)}
) } function Wrapper({ children }: any) { return ( {children} ) } render(, { wrapper: Wrapper }) // By the time this runs, the initial render will happen, and the query // will start immediately running by the time we can expect this expect(getRenderCount()).toBe(2) await waitFor(() => expect(screen.getByTestId('isFetching').textContent).toBe('false'), ) expect(getRenderCount()).toBe(3) }) test("Throws an error if you don't provide all hooks", async () => { const callBuildCreateApi = () => { const customCreateApi = buildCreateApi( coreModule(), reactHooksModule({ // @ts-ignore hooks: { useDispatch: createDispatchHook(MyContext), useSelector: createSelectorHook(MyContext), }, }), ) } expect(callBuildCreateApi).toThrowErrorMatchingInlineSnapshot( ` [Error: When using custom hooks for context, all 3 hooks need to be provided: useDispatch, useSelector, useStore. Hook useStore was either not provided or not a function.] `, ) }) test('allows passing createSelector instance', async () => { const memoize = vi.fn(lruMemoize) const createSelector = createSelectorCreator(memoize) const createApi = buildCreateApi( coreModule({ createSelector }), reactHooksModule({ createSelector }), ) const api = createApi({ baseQuery: async (arg: any) => { await delay(150) return { data: arg?.body ? { ...arg.body } : {}, } }, endpoints: (build) => ({ getUser: build.query<{ name: string }, number>({ query: () => ({ body: { name: 'Timmy' }, }), }), }), }) const storeRef = setupApiStore(api, {}, { withoutTestLifecycles: true }) await storeRef.store.dispatch(api.endpoints.getUser.initiate(1)) const selectUser = api.endpoints.getUser.select(1) expect(selectUser(storeRef.store.getState()).data).toEqual({ name: 'Timmy', }) expect(memoize).toHaveBeenCalledTimes(4) memoize.mockClear() function User() { const { isFetching } = api.endpoints.getUser.useQuery(1) return (
{String(isFetching)}
) } function Wrapper({ children }: any) { return {children} } render(, { wrapper: Wrapper }) await waitFor(() => expect(screen.getByTestId('isFetching').textContent).toBe('false'), ) // select() + selectFromResult expect(memoize).toHaveBeenCalledTimes(8) }) })