I was working on an app the other day using React Hooks and I happened upon a pattern I wanted to write up!
Let's set the scene. I have an
App with two components,
Warning. The goal is to show the warning when a user "touches" the box that says
Don't touch me!.
Our initial code looks like this. At the moment, the Warning always appears.
The first thing to do is note when someone mouses over the
Box component. If we can't detect that action then we can't do anything else. We'll use the
onMouseOver event and have it print a message to our console to make sure it's working.
If we toggle the console open and mouse over the box we should see a message.
But not everyone uses a mouse. A user may also focus on the element via keyboard. The
Box component is a single
div which is not a tabbable element. To make it tabbable we can set
tabIndex takes three possible values,
-1 which removes the element from tab order,
0 which adds it, and a positive value which allows you to explicitly set the tab order of the elements on the page.
The second thing we need to add is an
onFocus event. This looks exactly like our
onMouseOver event but it's the event that is fired when a keyboard user tabs over to an element.
Now that we can react to both a mouse or keyboard event, we'll want to somehow send that information to our
Warning component. This is where React hooks come into play!
In this example, we're going to leverage
useState. The syntax looks like this.
const [value, setValue] = useState("initial value")
In our app, we want a boolean value that will tell us whether or not to display the warning. So we create
isDanger which comes along with
setIsDanger, a setter function. We'll initialize
As it turns out, we can pass this setter function to
Box. And when we use it, it will alter the value of
isDanger is initialized in the parent component,
App. So even though the child component,
Box, triggered the value change, the state is still tied to
We can add a
console.log(isDanger) line in
App to confirm this behavior.
Now we can pass
isDanger to our
Warning component with full confidence that it will change value when we need it to. We'll use
isDanger to conditionally render the warning message. Otherwise, return
For accessibility, we'll add the role of
alert to the div. This means that whenever it appears on screen a screen reader will announce it.
And that's our example! If we want to make it a bit better we can change the events we're listening to. In our previous version, we set
isDanger to true and the warning shows up. However, the warning stays because we never set
isDanger back to false.
To handle the mouse interaction we can use
mouseLeave. For the keyboard, we want
onBlur. The great part about this is it doesn't change the complexity of our hook and passing state. We just reference the
setIsDanger function a few additional times.
This is a common pattern for using state hooks. And it isn't always easy to distill these into digestible examples! So I was very excited to be able to break down this one.