Use useToaster to toast toasts… a hot how to
How is that title for a hook? Ok, don’t react just yet, I promise I’m done with the wittiness attempts, internet as my witness.
This post assumes you are comfortable with ES6 & React and that you know React Hooks are a thing. If you know destructuring, you know React Hooks.
There are many solutions out there for creating toasts, also known as snackbars. Some are really cool looking, some have valuable features like not disappearing while hovered or a neat visual count down, and even all of the above.
While working on a small project recently, I wanted to implement toasts rapidly and without spending much time investigating libraries. Styling was also pretty low on my list of priorities, but important enough that I still wanted control over how things looked. Unfortunately, the majority of the existing solutions I found were overly featured and, for some, while looking at the source code, a little over-engineered.
Just give me a box and 1 function to add stuff in it.
So I built the useToaster React Hook. Here is what you can do with it.
Let’s build a counter app that will show a warning every time the number being rendered is a multiple of 5.
Prerequisites
We’ll start off from a new create-react-app
npx create-react-app counter5 && cd counter5 && npm start
and only modify the app.js file to look like this:
(We’re keeping the ./App.css for its text-align: center;)
Plug in the Toaster…
From here let’s just display a new toast every time the counter changes.
We’ll need to stop the app for a sec and install usetoaster.
// ctrl + c and then:
npm i usetoaster && npm start
Right back on track, we can start making toasts.
First we import the useToaster hook.
Instantiate it in the App component immediately above the useState hook.
This gives us a Toaster component and an addToast function.
The Toaster is just a box that will display whatever value you provide it via the addToast function. The main benefit of this approach is being able to pass the addToast function around and use it anywhere in your code.
Let’s add the Toaster component in the return statement
and make use of addToast by modifying the increase and decrease functions.
Styling aside, this works just fine, except the toasts are visible for 15 seconds before they finally disappear. That’s an awfully long time to wait for a warning to go away.
Of course you can click a toast to dismiss it, but let’s also reduce the delay to 5 seconds.
This feels just right, now let’s style the thing.
Styling
The philosophy here is that styling the Toaster is more about positioning and styling the toasts is more about the look.
Having said that, a key thing to remember is that the toasts are clickable divs wrapping whatever you pass to the addToast function. So you should let the width of the Toaster itself define the width of the toasts.
The toasts use 100% of the width of the Toaster by default. You should not change this behaviour, unless you have a good reason to.
So let’s just position the Toaster at the bottom of the page, and make it fixed so that its presence doesn’t disturb other elements in the DOM.
where toasterStyle is an object defined outside of the App component.
Alternatively, you could just create a .toaster class in App.css and just use it like so:
If that is the direction you prefer, then to style the toasts displayed in the Toaster, just create a .toast class in App.css and use it like so:
Because the content of a toast can be anything, I prefer to embrace React and create a Toast component, styled with styled-components. For the sake of not installing another dependency though, I’ll just style it inline:
And then use it in the addToast function:
This works just fine, but…
Understand that, behind the scene, each toast is wrapped in a div.
So depending on your styles, you might have a margin-collapsing situation, and the white space created by the margin might become clickable, which isn’t ideal.
If you run into this, then you can set the margin of the div that wraps the toast by passing a prop named toastMargin to the Toaster component.
Toast in moderation
Now let’s add the logic to only toast numbers that are multiples of 5.
Outside of the App component, we create a simple function to check if the number qualifies:
and use it in the increase and decrease functions:
This works beautifully but let’s refactor a little to avoid repetition:
Wonderful. So now we’re in a pretty happy place with the UI; toasts are no longer showing up on every click but only if the number is a multiple of 5, and they disappear by themselves after 5 seconds, or as soon as they’re being clicked.
toastList
As a final touch, we want to only toast a “toastable” number, if it isn’t already in the Toaster…
To achieve this sort of things, the useToaster React Hook also gives us a toastList, which is an array of toasts that are currently in the Toaster.
A toast in the toast list has the following shape:
{
“id”: 1,
“content”: “whatever you passed in addToast()”
}
If we had passed plain text instead of a react component to the addToast function, then it would have allowed us to modify our updateNumber function like so:
But we went with addToast(<Toast text={num} />) instead, so our toast shape is something like this:
{
“id”: 1,
“content”: {
“key”: null,
“ref”: null,
“props”: {
“text”: 5
},
“_owner”: null,
“_store”: {}
}
}
For scenarios like this, the addToast function also accepts a 2nd argument:
metadata
Metadata is meant to be whatever you need it to be.
In this case, we just want it to be a number representing the value of the toast. This allows us to modify our updateNumber function with:
where the notInToaster function is defined outside of the App component as:
All done
There you have it, a counter app that displays toasts only when the number is a multiple of 5 and isn’t already being toasted: