I tried everything but still for some reason the react state doesn't change, what is I am missing or what is the alternative approach? Code:
export const useCounter = (): UseCounterResult => {
const session = useSession();
const [list, setList] = useState<Customer[]>([]);
const [counter, setCounter] = useState(0);
const [limit, setLimit] = useState(10);
useEffect(() => {
(async () => {
const response = await getApiClient(session).listCustomers();
setList((response?.customers || []));
setCounter(response.todayCount);
setLimit(response.dailyLimit);
})();
}, [session]);
function addToList(newItem: Customer) {
setList((list) => [...newItem,...list]);
setCounter((counter) => counter + 1);
}
return {counter, limit, list, addToList};
};
And the UT I tried but doesn't work:
it.only("should add to list", async () => {
// arrange
(getApiClient as jest.Mock).mockReturnValue({
listCustomers: async () => ({
customers: [],
todayCount: 1,
dailyLimit: 5,
}),
});
// act
const { result, unmount, waitForNextUpdate, rerender } =
renderHook(useCounter);
act(() => {
result.current.addToList({
mobileNumber: "John",
receiverName: "+61456789123",
});
});
// await waitForNextUpdate();
// assert
await waitFor(() => expect(result.current.counter).toBe(2));
unmount();
});
I tried without wrapping into act and instead calling waitForNextUpdate after it, still didn't work, result.current.counter is 1 instead of 2.
Don't call
addToListuntil after you know that your async API call inside youruseEffectcompletes - because when that call completes it will overwrite the state that may have been set by anaddToListcall if the async call happened to be in progress.You can do that by asserting that
result.current.dailyLimitcontains the value returned from the API.After your test has asserted that, then go ahead and make your call to
addToListand perform your subsequent assertion