React Hooks useEffect with a real world example and tests

Julius Koronci
2 min readMay 20, 2019

--

@UPDATE this article is a bit outdated, please also look at https://github.com/testing-library/react-hooks-testing-library

I started using hooks in production a couple of weeks ago and so far it is a great experience. I received the following requirements in my application and decided to leverage hooks for it:

  1. When a user enters certain pages we need to change the document title.
  2. When the user leaves these pages we need to restore the title to whatever it was before.

While playing around, I discovered a really cool use case for the useEffect when used in combination with component unmount.

Let's look at our custom hook and explain it later :)

export const useDocumentTitle = (title: string) => {
useEffect(
()
=> {
const
currentTitle = getDocumentTitle();
setDocumentTitle(title);

return () => setDocumentTitle(currentTitle);
},
[],
);
};

Out custom hook is pretty basic, on componentDidMount we grab the current title and store it in a local variable. In the next step we update the documents title and on component unmount we restore the title to its previous value.

The simple fact that useEffect returns a callback which will be called on unmount is brilliant. In the callback, we have access to the lexical scope created on mount and hence we can restore the title easily when we leave the page. The other cool thing about this is, that you can nest the components and it will work nicely recursively.

Although this is a simple use case, I am using this approach in combination with context and useContext with a custom provider to override and restore top level components like navigation.

Since the title promises also tests, here it comes:

jest.mock('setDocumentTitle', () => ({
setDocumentTitle
: jest.fn(),
}));

describe('useDocumentTitle', () => {
const
Comp = () => {
useDocumentTitle('new title');
return <div />;
};

const wrapper = mount(<Comp />);

it('sets the document title on mount', () => {
expect(
setDocumentTitle)
.toHaveBeenLastCalledWith('new title');
});
it('restores title on unmount', () => {
wrapper.unmount();

expect(setDocumentTitle)
.toHaveBeenLastCalledWith('old title');
});
});

The only trick is to create a dummy component where we apply our hook and that's all we need.

Just a note, but when testing useState in components, you can only use mount with enzyme and you will have to call act to actually run the hooks, something like:

const wrapper = mount(<CoolComp />);act(() => {
jest.runAllImmediates();
});

Hope you liked the article, I just wanted to write down my findings somewhere in hope it can help someone running into similar use cases :)

--

--