Hone logo
Hone
Problems

Implementing a Custom useEventListener Hook in React with TypeScript

The useEventListener hook is a crucial utility for managing event listeners within functional React components. It simplifies the process of attaching and detaching event listeners to elements, preventing memory leaks and ensuring proper cleanup when components unmount. This challenge asks you to implement this hook from scratch, reinforcing your understanding of React hooks and event handling.

Problem Description

You are tasked with creating a custom useEventListener hook in React using TypeScript. This hook should accept two arguments:

  1. eventName: A string representing the name of the event to listen for (e.g., "click", "keydown", "scroll").
  2. callback: A function that will be executed when the specified event occurs.

The hook should:

  • Attach the provided callback as an event listener to the window object. This ensures the listener is global and persists across component re-renders.
  • Return undefined. The hook's primary purpose is side-effect management (attaching/detaching listeners), not returning a value.
  • Clean up the event listener when the component unmounts to prevent memory leaks. This is achieved using the useEffect hook with a cleanup function.
  • Handle the case where the eventName or callback changes. The hook should re-attach the listener with the new values.
  • Handle the case where the callback is null or undefined. In this scenario, the hook should remove the event listener if it's already attached.

Expected Behavior:

When the component mounts, the callback function should be attached as an event listener for the specified eventName on the window object. When the component unmounts, the event listener should be detached. If the eventName or callback changes during the component's lifecycle, the hook should update the event listener accordingly. If the callback is null/undefined, the listener should be removed.

Examples

Example 1:

Input: eventName = "click", callback = (event: MouseEvent) => console.log("Clicked!", event.target)
Output: (No direct output, but the console will log "Clicked!" and the target element when the window is clicked)
Explanation:  A click event listener is attached to the window.  Each click on the window will trigger the callback, logging the event and the clicked element.

Example 2:

Input: eventName = "keydown", callback = (event: KeyboardEvent) => console.log("Key pressed:", event.key)
Output: (No direct output, but the console will log the pressed key when a key is pressed)
Explanation: A keydown event listener is attached to the window. Each key press will trigger the callback, logging the pressed key.

Example 3:

Input: eventName = "scroll", callback = (event: Event) => console.log("Scrolled")
Output: (No direct output, but the console will log "Scrolled" when the window is scrolled)
Explanation: A scroll event listener is attached to the window. Each scroll event will trigger the callback, logging "Scrolled".

Example 4: (Edge Case - Removing Listener)

Input: eventName = "click", callback = (event: MouseEvent) => console.log("Clicked!", event.target), then later eventName = "click", callback = null
Output: (No direct output, but the previous click listener is removed)
Explanation: Initially, a click listener is attached.  Then, the callback is set to null, which triggers the removal of the previously attached click listener.

Constraints

  • The hook must be implemented using TypeScript.
  • The event listener must be attached to the window object.
  • The hook must correctly handle component mounting, unmounting, and updates to the eventName and callback.
  • The hook must prevent memory leaks by properly detaching the event listener on unmount.
  • The hook should not return any value directly.
  • The callback function should receive the standard event object for the specified eventName.

Notes

  • Consider using the useEffect hook to manage the event listener lifecycle.
  • The cleanup function within useEffect is crucial for detaching the event listener.
  • Pay close attention to how the hook handles changes in eventName and callback. You'll need to ensure that the listener is updated or removed appropriately.
  • Think about how to efficiently update the event listener when the dependencies change. Avoid unnecessary re-attachments.
  • Remember to type the event object correctly based on the eventName. For example, use MouseEvent for "click" and KeyboardEvent for "keydown".
Loading editor...
typescript