I have a simple custom hook like so:
import { useState } from 'react';
export default function useOpenClose(initial = false) {
const [isOpen, setOpen] = useState(initial);
const open = () => { setOpen(true); }
const close = () => { setOpen(false); }
return [isOpen, { open, close }];
}
I have a test that's somewhat passing. When I have a test that updates state, it doesn't seem to work.
The test in question:
import { renderHook, act } from '@testing-library/react-hooks';
import useOpenClose from './useOpenClose';
describe('useOpenClose', () => {
const { result: { current } } = renderHook(() => useOpenClose());
const [isOpen, { open, close }] = current;
test('Should have initial value of false', () => {
expect(isOpen).toBe(false);
});
test('Should update value to true', () => {
act(() => open())
console.log(isOpen)
})
});
Test 1 passes, but when I console.log(isOpen) for test 2, isOpen remains false.
Does it have anything to do with the array destructuring? Is it immutable? Or is it the way I'm using act()?
Hi @davidgtu,
You cannot destructure the values as it locks in their current value. It's like having
const result = { current: "example" }
const example = result.current
result.current = "something else"
expect(example).toBe(result.current)
and expecting the values to still be the same.
Try making your test:
describe('useOpenClose', () => {
const { result } = renderHook(() => useOpenClose());
test('Should have initial value of false', () => {
console.log(result.current[0])
});
test('Should update value to true', () => {
act(() => result.current[1].open())
console.log(result.current[0])
})
});
It looks a little but nasty when you have an array as the result of your custom hook, but unfortunately it's a symptom of your hook design (which I'm not saying is bad). You could make it a bit more readible by adjusting the hook callback slightly:
describe('useOpenClose', () => {
const { result } = renderHook(() => {
const [isOpen, actions] = useOpenClose();
return { isOpen, ...actions };
});
test('Should have initial value of false', () => {
console.log(result.current.isOpen)
});
test('Should update value to true', () => {
act(() => result.current.open())
console.log(result.current.isOpen)
})
});
@mpeyper That was incredibly helpful. Thank you so much.
Maybe this is worth to record in the docs as might happen to others (like me just now :sweat_smile:)
Great idea @gillchristian. PRs always welcome ;)
:heavy_check_mark: :tada:
This can be closed now :smirk:
Most helpful comment
Hi @davidgtu,
You cannot destructure the values as it locks in their current value. It's like having
and expecting the values to still be the same.
Try making your test:
It looks a little but nasty when you have an array as the result of your custom hook, but unfortunately it's a symptom of your hook design (which I'm not saying is bad). You could make it a bit more readible by adjusting the hook callback slightly: