Recoil
Recoil copied to clipboard
Testing custom hook with recoil atom inside
I have a custom hook useImageActions
which returns a set of functions
handleAddImages
handleUpdateImagesPosition
handleDelete
handleFinishEdit
Each function makes some things like mutations with GQL, or else and update the state with the result with recoil.
From your documentation i used RecoilObserver
and i came up with this solution
describe("", () => {
it("", async () => {
const onChange = jest.fn();
const atom = imagesAtom(PRODUCT_SET_ID);
const wrapper = ({ children }: PropsWithChildren<{}>) => (
<RecoilRoot initializeState={({set}) => set(atom, INIT_IMAGES)}>
<RecoilObserver node={atom} onChange={onChange}/>
<Providers>
<MockedProvider addTypename={false} mocks={[mock]}>
{children}
</MockedProvider>
</Providers>
</RecoilRoot>
);
const { result } = renderHook(
() => useImageActions({ productSetId: PRODUCT_SET_ID }),
{ wrapper },
);
await act(async () => {
await result.current.handleAddImages(ADD_IMAGES);
});
expect(onChange).toHaveBeenCalledTimes(2);
expect(onChange).toHaveBeenCalledWith(INIT_IMAGES);
expect(onChange).toHaveBeenCalledWith([...INIT_IMAGES, ...ADD_IMAGES]);
});
});
As you can see i initialized first state with initializeState
, i also provided atom
to the observer
I ve been using renderHook
which gonna be rendered behind the scene in some test component.
For my atom i use families because i need to, and PRODUCT_SET_ID
is unique ID for each "family" for that purposes.
I am using the same PRODUCT_SET_ID
for atom
, and i am using the same imagesAtom
as i use in the project.
it looks like this
export const imagesAtom = atomFamily<ExtendedImage[], string>({
key: "images",
default: [],
});
My hook uses this atom as well inside, to provide a possibility to the functions (image actions) to change the state.
So basically, my expectations that the hook and RecoilRoot
and RecoilObserver
should share the same atom family, because all of them use the same imagesAtom
and all of them have the same PRODUCT_SET_ID
. But i guess it is not true
The issue It looks like they are disconnected. During the test when i invoke
await act(async () => {
await result.current.handleAddImages(ADD_IMAGES);
});
I get
amount of calls 1
and last toHaveBeenCalledWith
does not have ADD_IMAGES
its still just have INIT_IMAGES
only
My question is how can i make this kind of connection in the right way? What am i missing? Or what i am doing it is an antipattern and it should be used in some other way?