25 May, 2022#Reactjs#web

Making sense of useEvent

A new React hook is a pretty big deal. Since the debut of the original 10 hooks in React 16.8, the only time React introduced more hooks was for the recent release of React 18. And considering how React 18 marks a new foundation and the culmination of many years of research, it's noteworthy that React 18 has only introduced 5 additional hooks.

So it was a big news and a surprise when an RFC appeared for a new hook.

The RFC, posted earlier this month (May 2022), introduces a hook called useEvent, which, per Dan Abramov's tweet, "might have been the missing piece missing piece in the original Hooks release." A bold statement! And the goal of useEvent, as stated in the RFC's PR description, is to let developers "define event handlers that can read the latest props/state but have always stable function identity. Event handlers defined with useEvent don't break memoization and don't retrigger effects." This blog post is not about simply reiterating what the RFC says, so here's the formatted RFC document. Suffice it to say that the RFC has garnered a great deal of attention, mostly positive since the new hook was motivated by and directly addresses concrete pain points.

But a lot of people, even those who generally approve it as a welcome solution to common problems, have found the naming of useEvent confusing. Why call it use-Event when it's meant to create event handlers? In fact, isn't this essentially to create a closure with a stable reference so that it can always read the latest prop and state values without breaking memoization--what do browser events have anything to do with this? Indeed, so many people raised this point in the discussion that Dan Abramov had to add a new FAQ item in the PR description to directly address it: "What we're looking for in naming". Still, more people came to question the new hook's naming.

I, too, had ongoing reservations about the naming. The provided rationale--that 1) the name for a commonly used hook should be short (two-word names like useEventHandler are undesirable) and 2) the name should sound sensible as a "type" of function (i.e. the "type" of a function for useFunction would be "function function", which is awkward)--was clearly not strong enough to stop people from bringing up the same concern over and over again. useState returns an state tuple. useRef returns a "ref" object. useCallback returns a memoised callback function. But useEvent doesn't return a event... so what gives?

A couple of weeks later, Dan Abramov unveiled a draft of the new React docs page on effects. An exciting news, so I dropped whatever I was doing at the moment and read the article. It contained many interesting points and helpful insights about what "effects" are in React and how useEffect can help. But most importantly, reading the article on "effects" helped me to finally get the naming and API of useEvent.

In the article, "effects" in React are defined as what "let you specify side effects that are caused by rendering itself." What caught my eyes here is the expression that these "effects" specify the content of a certain type of side effects, namely those caused by rendering. What useEffect does here is allowing us developers to "use" an "effect," specified in the body of a function that we pass to it as a parameter, by running that "effect" after every component render--or conditionally per dependency array.

What's interesting in that article is how such "effects" are contrasted with "events":

What are effects and how are they different from events?

And while "events" are mostly discussed in terms of event handlers, this contrast with "effects" suggests that these "events" define the content of certain side effects, rather than denoting their causes. Consider the following example from the article:

Sending a message in the chat is an event because it is directly caused by the user clicking a specific button. However, setting up a server connection is an effect because it needs to happen regardless of which interaction caused the component to appear.

Note that the "event" in this example ("sending a message in the chat") is not the browser event itself, namely the button click event, but the content of a side effect that is caused by it.

If "events" in React are understood not as browser events, which directly correspond to specific user actions such as button click, but as what specify side effects caused by such user actions, then the naming and API of useEvent start to make sense: Just as in useEffect, we'll pass an "event", specified in the body of a function, to useEvent as a parameter. But because such an "event" must be triggered by user actions elsewhere, useEvent still needs to return a function that can be passed to the source of those user actions.

In short, "events" in React are not browser events but rather the side effects of handling such browser events. This, I believe, is what justifies the naming and API of useEvent.1

TL;DR: "Events" in React correspond to a type of side effects, like "effects", rather than literal browser events. "Events" specify the content of side effects caused by user actions and "effects", by rendering. Seen in this light, the useEvent naming and API start to make sense.

  1. After all, I think it's still unfortunate that React has opted for overloading the term "event", which has forever had a concrete meaning and use in the web in general. I also think that the documentation can do a better job at clarifying what "events" are in React's mental model. That said, I love the "effect-event" pair for classifying side effects.