Handling global notifications with React’s Context API
In this article, we aim to present a use case on how to use React Hooks and Context API in order to show notifications globally. The example presented here creates a way to show error messages (namely API error messages) reported by feature components (our pages) when a request is performed. The approach presented here was inspired by the pattern extinction level event. In the end, we also explain how to easily test some of the components created. You can find the full code examples described in this blogpost in this codesandbox.
We have a folder structure similar to the following:
src/App.js
is the entry point for our React application, similar to what is created with Create React App.
The src/components
folder contains basic react components that range from Button to Modal, Header, Footer, etc (for such a simple project we didn't feel the need to split using something such as atomic design). The features
folder contains the pages, that are smart components that handle network requests and the structure of the pages.
API requests, custom React Hooks and Providers created with Context API can be found in a common
folder, as they can be shared through the components (namely, features).
OK, but what do we want exactly to do? a bottom-up approach to push errors and make them visible. Context seemed a good candidate to handle that.
Since we just need to show one error message at a time we can just save that message
(string) and the status
code in context. The functions to add and remove (clear) the error are also exposed in context through the provider.
We need to add our APIErrorProvider
pretty much we add other providers such as Authentication provider or Material ui to our main App component.
Note that we also add
APIErrorNotification
component here which is the component that will render our popup notification with the error.
Using your own hook and avoid having to do useContext
in every component
The useAPIError
custom hook does not do anything fancy but makes the code more readable and avoids having to write too many lines. It exposes the addError
and removeError
functions and the error
to used by APIErrorNotification
.
Now we can use our custom hook in a feature page, such as the following example. Here we are doing a request to an API when the component mounts ( useEffect
with no dependencies). If the request fails we call the addError
method in our Provider and the context gets updated.
Once the context gets updated, the APIErrorNotification
is notified that its value changed and rerenders. We can show a message in a paragraph (or markup, why not?), use a modal, a toast / snackbar or other. In this case, imagine that we have a modal component that just shows up when there's an error:
Note that the way to remove the error message is to submits the modal, which will update the context and trigger a rerender where modal will not show up.
We are going to use @testing-library/react in the following examples. It is really simple to use, and the whole idea is to test as a user which helps to identify the use cases and meet user acceptance criteria. There is also react-hooks-testing-library which might help to test our custom hooks.
From this example you can see that:
- We can pass a custom error message or just use a mocked one;
- We are extending the functions provided by
@testing-library/react
(getByTestId
,queryByTestId
, etc) which means that we testaddErrorSpy
andremoveErrorSpy
usages properly.
In order to use it on a test we can do the following:
For example, if we add the data-testid
attribute to the submit button on the APIErrorNotification
component we can trigger it and confirm that removeErrorSpy
was called when the button was pressed.
Note that we wanted to show how it is possible to test the functions of the provider. However, in this particular example we could just have tested that the modal was not visible anymore, e.g.
We hope this gave some ideas on how to use Context API and on how to test your use cases. Happy coding!