The UseEvent Hook
Published: 2022-09-05 08:58:18

This article needs prior understanding of Hooks. So, if you are not already familiar please checkout the [official docs](https://reactjs.org/docs/hooks-intro.html).


I came across this hook while reviewing a PR of one of my colleague [ciprian](https://dev.to/cipriananin). One of my colleague introduced a hook in his PR and we had a long healthy technical discussion around that as it was solving [this issue](https://github.com/facebook/react/issues/14099).

###Motivation for introducing useEvent

The React core team published a Request for Comment (RFC) for a new React hook: `useEvent`. This post attempts to capture what this hook is and how it works.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7wnjxtbfjtwq2flmgqqs.jpg)

Also here is the proposal for [useEvent hook](https://github.com/reactjs/rfcs/blob/useevent/text/0000-useevent.md) and its not been released yet (by the time I am writing this post) and its behavior could change.

### The real problem
let’s wrap our heads around the problem before we jump into what `useEvent` is. React’s execution model is largely powered by comparing the current and previous values of `state`/`props`. This happens in components and in hooks like `useEffect`, `useMemo`, and `useCallback`.
Lets see this example to make better understanding of the problem.

#### Example

This `onClick` event handler needs to read the currently typed text:

```javascript
function Chat() {
  const [text, setText] = useState('');

  // 🟡 Always a different function
  const onClick = () => {
    sendMessage(text);
  };

  return <SendButton onClick={onClick} />;
}
```

Let's say you want to optimize `SendButton` by wrapping it in `React.memo`. For this to work, the props need to be shallowly equal between re-renders. The `onClick` function will have a different function identity on every re-render, so it will break memoization.
The usual way to approach a problem like this is to wrap the function into `useCallback` hook to preserve the function identity. However, it wouldn't help in this case because onClick needs to read the latest text:

```javascript
function Chat() {
  const [text, setText] = useState('');

  // 🟡 A different function whenever `text` changes
  const onClick = useCallback(() => {
    sendMessage(text);
  }, [text]);

  return <SendButton onClick={onClick} />;
}
```

The text changes with every keystroke, so `onClick` will still be a different function on every keystroke. (We can't remove text from the `useCallback` dependencies because otherwise the `onClick` handler would always "see" the initial text.

### What's the solution
A new hook `useEvent` is being proposed to ensure we have a stable reference to a function without having to create a new function based on its dependents.

#### What is `useEvent` hook?
A Hook to define an event handler with an <mark>**always-stable**</mark> function identity.

By comparison, `useEvent` does not take a dependency array and always returns the same stable function, even if the text changes. Nevertheless, text inside `useEvent` will reflect its latest value:

```javascript
function Chat() {
  const [text, setText] = useState('');

  // ✅ Always the same function (even if `text` changes)
  const onClick = useEvent(() => {
    sendMessage(text);
  });

  return <SendButton onClick={onClick} />;
}
```
As a result, memoizing `SendButton` will now work because its `onClick` prop will always receive the same function.

---

###When to use useCallBack?

You must be curious about what would be the use-cases where `useCallBack` will be useful after we have the `useEvent` hook? I had the same question back then.

Well, `useCallaback` will be used for memoizing render functions. **For example:**

``` javascript
  const renderItem = useCallback(() => (
    <span>{itemName}</span>
  ), [itemName])
```

**Why**? because useEvent cannot be used for this scenario as <mark>it would keep the same reference for render function</mark>, and this is not desired, because we need a changing ref so that we know when to rerender.

Event handlers wrapped in `useEvent` will throw if called during render. (Calling it from an effect or at any other time is fine.) So it is enforced that during rendering these functions are treated as opaque and never called. This makes it safe to preserve their identity despite the changing `props`/`state` inside.

You can see many more evidences on that [issue](https://github.com/facebook/react/issues/14099) and [RFC](https://github.com/reactjs/rfcs/blob/useevent/text/0000-useevent.md) itself, I am not listing those here to keep this post concise. Overall, I think this is going to be a great addition to the React ecosystem.

---

Here is what my colleague did to mock the same behavior that `useEvent` would probably be doing soon.

```javascript
export default function useEventCallback<Args extends unknown[], Return>(
  fn: (...args: Args) => Return,
): (...args: Args) => Return {
  // ref is not initialized, in order to ensure that we can't call this in the render phase
  // ref.current will be undefined if we call the `fn` during render phase (as a render function)
  const ref = useRef<(typeof fn) | undefined>(undefined);
  
  useLayoutEffect(() => {
    ref.current = fn;
  });

  return useCallback(
    (...args: Args) =>
    // make sure that the value of `this` provided for the call to fn is not `ref`
      ref.current.apply(void 0, args),
    [],
  );
}

```

***
<center> Hit the :heart: if it was useful.
Please share your feedback so I can make my blog posts more helpful. :blush:
</center>
***